Merge changes from topic "getsuggestedframerate" into main

* changes:
  Adds getSuggestedFrameRate api
  Adds support for getSuggestedFrameRate api
  adds a flag for getSuggestedFrameRates api
diff --git a/ACTIVITY_SECURITY_OWNERS b/ACTIVITY_SECURITY_OWNERS
new file mode 100644
index 0000000..c39842e
--- /dev/null
+++ b/ACTIVITY_SECURITY_OWNERS
@@ -0,0 +1,2 @@
+haok@google.com
+wnan@google.com
\ No newline at end of file
diff --git a/AconfigFlags.bp b/AconfigFlags.bp
index c775012..26fbd27 100644
--- a/AconfigFlags.bp
+++ b/AconfigFlags.bp
@@ -1560,6 +1560,7 @@
     name: "android.crashrecovery.flags-aconfig-java",
     aconfig_declarations: "android.crashrecovery.flags-aconfig",
     defaults: ["framework-minus-apex-aconfig-java-defaults"],
+    min_sdk_version: "35",
     apex_available: [
         "//apex_available:platform",
         "com.android.crashrecovery",
diff --git a/Android.bp b/Android.bp
index bfe749a..26d0d65 100644
--- a/Android.bp
+++ b/Android.bp
@@ -144,9 +144,6 @@
         // For the generated R.java and Manifest.java
         ":framework-res{.aapt.srcjar}",
 
-        // Java/AIDL sources to be moved out to CrashRecovery module
-        ":framework-crashrecovery-sources",
-
         // etc.
         ":framework-javastream-protos",
         ":statslog-framework-java-gen", // FrameworkStatsLog.java
@@ -432,7 +429,12 @@
     name: "framework-non-updatable-unbundled-impl-libs",
     static_libs: [
         "framework-location.impl",
-    ],
+    ] + select(soong_config_variable("ANDROID", "release_crashrecovery_module"), {
+        "true": [],
+        default: [
+            "framework-platformcrashrecovery.impl",
+        ],
+    }),
     sdk_version: "core_platform",
     installable: false,
 }
@@ -565,7 +567,12 @@
         "documents-ui-compat-config",
         "calendar-provider-compat-config",
         "contacts-provider-platform-compat-config",
-    ],
+    ] + select(soong_config_variable("ANDROID", "release_crashrecovery_module"), {
+        "true": [],
+        default: [
+            "framework-platformcrashrecovery-compat-config",
+        ],
+    }),
 }
 
 platform_compat_config {
diff --git a/INTENT_OWNERS b/INTENT_OWNERS
index 58b5f2a..c828215 100644
--- a/INTENT_OWNERS
+++ b/INTENT_OWNERS
@@ -1,3 +1,4 @@
 include /PACKAGE_MANAGER_OWNERS
 include /services/core/java/com/android/server/wm/OWNERS
 include /services/core/java/com/android/server/am/OWNERS
+include /ACTIVITY_SECURITY_OWNERS
\ No newline at end of file
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index fea2b7b..a92a543 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -18,7 +18,7 @@
                tests/
                tools/
 bpfmt = -d
-ktfmt = --kotlinlang-style --include-dirs=services/permission,packages/SystemUI
+ktfmt = --kotlinlang-style --include-dirs=services/permission,packages/SystemUI,libs/WindowManager/Shell/src/com/android/wm/shell/freeform
 
 [Hook Scripts]
 checkstyle_hook = ${REPO_ROOT}/prebuilts/checkstyle/checkstyle.py --sha ${PREUPLOAD_COMMIT}
diff --git a/apct-tests/perftests/core/src/android/conscrypt/conscrypt/ClientSocketPerfTest.java b/apct-tests/perftests/core/src/android/conscrypt/conscrypt/ClientSocketPerfTest.java
index 3577fcd..f20b170 100644
--- a/apct-tests/perftests/core/src/android/conscrypt/conscrypt/ClientSocketPerfTest.java
+++ b/apct-tests/perftests/core/src/android/conscrypt/conscrypt/ClientSocketPerfTest.java
@@ -194,7 +194,7 @@
     /**
      * Simple benchmark for the amount of time to send a given number of messages
      */
-    // @Test Temporarily disabled
+    @Test
     @Parameters(method = "getParams")
     public void time(Config config) throws Exception {
         reset();
diff --git a/apct-tests/perftests/core/src/android/conscrypt/conscrypt/ServerSocketPerfTest.java b/apct-tests/perftests/core/src/android/conscrypt/conscrypt/ServerSocketPerfTest.java
index ac57100..af3c405 100644
--- a/apct-tests/perftests/core/src/android/conscrypt/conscrypt/ServerSocketPerfTest.java
+++ b/apct-tests/perftests/core/src/android/conscrypt/conscrypt/ServerSocketPerfTest.java
@@ -198,7 +198,7 @@
         executor.awaitTermination(5, TimeUnit.SECONDS);
     }
 
-    // @Test Temporarily disabled
+    @Test
     @Parameters(method = "getParams")
     public void throughput(Config config) throws Exception {
         setup(config);
diff --git a/apex/jobscheduler/framework/aconfig/job.aconfig b/apex/jobscheduler/framework/aconfig/job.aconfig
index 5f55075..debd850 100644
--- a/apex/jobscheduler/framework/aconfig/job.aconfig
+++ b/apex/jobscheduler/framework/aconfig/job.aconfig
@@ -25,8 +25,15 @@
 }
 
 flag {
-   name: "cleanup_empty_jobs"
+   name: "handle_abandoned_jobs"
    namespace: "backstage_power"
-   description: "Enables automatic cancellation of jobs due to leaked JobParameters, reducing unnecessary battery drain and improving system efficiency. This includes logging and traces for better issue diagnosis."
-   bug: "349688611"
+   description: "Detect, report and take action on jobs that maybe abandoned by the app without calling jobFinished."
+   bug: "372529068"
+}
+
+flag {
+    name: "ignore_important_while_foreground"
+    namespace: "backstage_power"
+    description: "Ignore the important_while_foreground flag and change the related APIs to be not effective"
+    bug: "374175032"
 }
diff --git a/apex/jobscheduler/framework/java/android/app/job/IJobCallback.aidl b/apex/jobscheduler/framework/java/android/app/job/IJobCallback.aidl
index 11d17ca..887f812 100644
--- a/apex/jobscheduler/framework/java/android/app/job/IJobCallback.aidl
+++ b/apex/jobscheduler/framework/java/android/app/job/IJobCallback.aidl
@@ -87,11 +87,12 @@
     void jobFinished(int jobId, boolean reschedule);
 
     /*
-     * Inform JobScheduler to force finish this job because the client has lost
-     * the job handle. jobFinished can no longer be called from the client.
+     * Inform JobScheduler that this job may have been abandoned because the client process
+     * has lost strong references to the JobParameters object without calling jobFinished.
+     *
      * @param jobId Unique integer used to identify this job
      */
-    void forceJobFinished(int jobId);
+    void handleAbandonedJob(int jobId);
 
     /*
      * Inform JobScheduler of a change in the estimated transfer payload.
diff --git a/apex/jobscheduler/framework/java/android/app/job/JobInfo.java b/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
index 5f57c39..cc2d104 100644
--- a/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
+++ b/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
@@ -809,9 +809,21 @@
     }
 
     /**
+     * <p class="caution"><strong>Note:</strong> Beginning with
+     * {@link android.os.Build.VERSION_CODES#B}, this flag will be ignored and no longer
+     * function effectively, regardless of the calling app's target SDK version.
+     * Calling this method will always return {@code false}.
+     *
      * @see JobInfo.Builder#setImportantWhileForeground(boolean)
+     *
+     * @deprecated Use {@link #isExpedited()} instead.
      */
+    @FlaggedApi(Flags.FLAG_IGNORE_IMPORTANT_WHILE_FOREGROUND)
+    @Deprecated
     public boolean isImportantWhileForeground() {
+        if (Flags.ignoreImportantWhileForeground()) {
+            return false;
+        }
         return (flags & FLAG_IMPORTANT_WHILE_FOREGROUND) != 0;
     }
 
@@ -2124,6 +2136,13 @@
          * <p>
          * Jobs marked as important-while-foreground are given {@link #PRIORITY_HIGH} by default.
          *
+         * <p class="caution"><strong>Note:</strong> Beginning with
+         * {@link android.os.Build.VERSION_CODES#B}, this flag will be ignored and no longer
+         * function effectively, regardless of the calling app's target SDK version.
+         * {link #isImportantWhileForeground()} will always return {@code false}.
+         * Apps should use {link #setExpedited(boolean)} with {@code true} to indicate
+         * that this job is important and needs to run as soon as possible.
+         *
          * @param importantWhileForeground whether to relax doze restrictions for this job when the
          *                                 app is in the foreground. False by default.
          * @see JobInfo#isImportantWhileForeground()
@@ -2131,6 +2150,12 @@
          */
         @Deprecated
         public Builder setImportantWhileForeground(boolean importantWhileForeground) {
+            if (Flags.ignoreImportantWhileForeground()) {
+                Log.w(TAG, "Requested important-while-foreground flag for job" + mJobId
+                        + " is ignored and takes no effect");
+                return this;
+            }
+
             if (importantWhileForeground) {
                 mFlags |= FLAG_IMPORTANT_WHILE_FOREGROUND;
                 if (mPriority == PRIORITY_DEFAULT) {
diff --git a/apex/jobscheduler/framework/java/android/app/job/JobParameters.java b/apex/jobscheduler/framework/java/android/app/job/JobParameters.java
index 31d2ecd..b6f0c04 100644
--- a/apex/jobscheduler/framework/java/android/app/job/JobParameters.java
+++ b/apex/jobscheduler/framework/java/android/app/job/JobParameters.java
@@ -16,6 +16,7 @@
 
 package android.app.job;
 
+import android.annotation.FlaggedApi;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -33,8 +34,8 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.PersistableBundle;
-import android.os.RemoteException;
 import android.os.Process;
+import android.os.RemoteException;
 import android.system.SystemCleaner;
 import android.util.Log;
 
@@ -121,6 +122,15 @@
             JobProtoEnums.INTERNAL_STOP_REASON_ANR; // 12.
 
     /**
+     * The job ran for at least its minimum execution limit and the app lost the strong reference
+     * to the {@link JobParameters}. This could indicate that the job is empty because the app
+     * can no longer call {@link JobService#jobFinished(JobParameters, boolean)}.
+     * @hide
+     */
+    public static final int INTERNAL_STOP_REASON_TIMEOUT_ABANDONED =
+            JobProtoEnums.INTERNAL_STOP_REASON_TIMEOUT_ABANDONED; // 13.
+
+    /**
      * All the stop reason codes. This should be regarded as an immutable array at runtime.
      *
      * Note the order of these values will affect "dumpsys batterystats", and we do not want to
@@ -144,6 +154,7 @@
             INTERNAL_STOP_REASON_SUCCESSFUL_FINISH,
             INTERNAL_STOP_REASON_USER_UI_STOP,
             INTERNAL_STOP_REASON_ANR,
+            INTERNAL_STOP_REASON_TIMEOUT_ABANDONED,
     };
 
     /**
@@ -166,6 +177,7 @@
             case INTERNAL_STOP_REASON_SUCCESSFUL_FINISH: return "successful_finish";
             case INTERNAL_STOP_REASON_USER_UI_STOP: return "user_ui_stop";
             case INTERNAL_STOP_REASON_ANR: return "anr";
+            case INTERNAL_STOP_REASON_TIMEOUT_ABANDONED: return "timeout_abandoned";
             default: return "unknown:" + reasonCode;
         }
     }
@@ -269,6 +281,25 @@
      */
     public static final int STOP_REASON_ESTIMATED_APP_LAUNCH_TIME_CHANGED = 15;
 
+    /**
+     * The job used up its maximum execution time and timed out. The system also detected that the
+     * app can no longer call {@link JobService#jobFinished(JobParameters, boolean)} for this job,
+     * likely because the strong reference to the job handle ({@link JobParameters}) passed to it
+     * via {@link JobService#onStartJob(JobParameters)} was lost. This can occur even if the app
+     * called {@link JobScheduler#cancel(int)}, {@link JobScheduler#cancelAll()}, or
+     * {@link JobScheduler#cancelInAllNamespaces()} to stop an active job while losing strong
+     * references to the job handle. In this case, the job is not necessarily abandoned. However,
+     * the system cannot distinguish such cases from truly abandoned jobs.
+     * <p>
+     * It is recommended that you use {@link JobService#jobFinished(JobParameters, boolean)} or
+     * return false from {@link JobService#onStartJob(JobParameters)} to stop an active job. This
+     * will prevent the occurrence of this stop reason and the need to handle it. The primary use
+     * case for this stop reason is to report a probable case of a job being abandoned.
+     * <p>
+     */
+    @FlaggedApi(Flags.FLAG_HANDLE_ABANDONED_JOBS)
+    public static final int STOP_REASON_TIMEOUT_ABANDONED = 16;
+
     /** @hide */
     @IntDef(prefix = {"STOP_REASON_"}, value = {
             STOP_REASON_UNDEFINED,
@@ -287,6 +318,7 @@
             STOP_REASON_USER,
             STOP_REASON_SYSTEM_PROCESSING,
             STOP_REASON_ESTIMATED_APP_LAUNCH_TIME_CHANGED,
+            STOP_REASON_TIMEOUT_ABANDONED,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface StopReason {
@@ -361,6 +393,12 @@
     }
 
     /**
+     * Returns the reason {@link JobService#onStopJob(JobParameters)} was called on this job.
+     * <p>
+     * Apps should not rely on the stop reason for critical decision-making, as additional stop
+     * reasons may be added in subsequent Android releases. The primary intended use of this method
+     * is for logging and diagnostic purposes to gain insights into the causes of job termination.
+     * <p>
      * @return The reason {@link JobService#onStopJob(JobParameters)} was called on this job. Will
      * be {@link #STOP_REASON_UNDEFINED} if {@link JobService#onStopJob(JobParameters)} has not
      * yet been called.
@@ -770,7 +808,7 @@
                 return;
             }
             try {
-                mCallback.forceJobFinished(mJobId);
+                mCallback.handleAbandonedJob(mJobId);
             } catch (Exception e) {
                 Log.wtf(TAG, "Could not destroy running job", e);
             }
diff --git a/apex/jobscheduler/framework/java/android/app/job/JobServiceEngine.java b/apex/jobscheduler/framework/java/android/app/job/JobServiceEngine.java
index 5f80c52..69b83cc 100644
--- a/apex/jobscheduler/framework/java/android/app/job/JobServiceEngine.java
+++ b/apex/jobscheduler/framework/java/android/app/job/JobServiceEngine.java
@@ -165,11 +165,11 @@
                 case MSG_EXECUTE_JOB: {
                     final JobParameters params = (JobParameters) msg.obj;
                     try {
-                        if (Flags.cleanupEmptyJobs()) {
+                        if (Flags.handleAbandonedJobs()) {
                             params.enableCleaner();
                         }
                         boolean workOngoing = JobServiceEngine.this.onStartJob(params);
-                        if (Flags.cleanupEmptyJobs() && !workOngoing) {
+                        if (Flags.handleAbandonedJobs() && !workOngoing) {
                             params.disableCleaner();
                         }
                         ackStartMessage(params, workOngoing);
@@ -196,7 +196,7 @@
                     IJobCallback callback = params.getCallback();
                     if (callback != null) {
                         try {
-                            if (Flags.cleanupEmptyJobs()) {
+                            if (Flags.handleAbandonedJobs()) {
                                 params.disableCleaner();
                             }
                             callback.jobFinished(params.getJobId(), needsReschedule);
diff --git a/apex/jobscheduler/service/java/com/android/server/AppStateTrackerImpl.java b/apex/jobscheduler/service/java/com/android/server/AppStateTrackerImpl.java
index 33f6899..ecb9a73 100644
--- a/apex/jobscheduler/service/java/com/android/server/AppStateTrackerImpl.java
+++ b/apex/jobscheduler/service/java/com/android/server/AppStateTrackerImpl.java
@@ -444,8 +444,13 @@
     private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
+            final String action = intent.getAction();
+            if (action == null) {
+                return;
+            }
+
             final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
-            switch (intent.getAction()) {
+            switch (action) {
                 case Intent.ACTION_USER_REMOVED:
                     if (userId > 0) {
                         mHandler.doUserRemoved(userId);
diff --git a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
index a37779e..3e650da 100644
--- a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
+++ b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
@@ -666,7 +666,12 @@
 
     private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
         @Override public void onReceive(Context context, Intent intent) {
-            switch (intent.getAction()) {
+            final String action = intent.getAction();
+            if (action == null) {
+                return;
+            }
+
+            switch (action) {
                 case ConnectivityManager.CONNECTIVITY_ACTION: {
                     updateConnectivityState(intent);
                 } break;
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 97c6e25..4c1951a 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -5864,6 +5864,9 @@
             pw.print(android.app.job.Flags.FLAG_BACKUP_JOBS_EXEMPTION,
                     android.app.job.Flags.backupJobsExemption());
             pw.println();
+            pw.print(android.app.job.Flags.FLAG_IGNORE_IMPORTANT_WHILE_FOREGROUND,
+                    android.app.job.Flags.ignoreImportantWhileForeground());
+            pw.println();
             pw.decreaseIndent();
             pw.println();
 
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerShellCommand.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerShellCommand.java
index ac240cc..68303e8 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerShellCommand.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerShellCommand.java
@@ -436,6 +436,9 @@
             case android.app.job.Flags.FLAG_BACKUP_JOBS_EXEMPTION:
                 pw.println(android.app.job.Flags.backupJobsExemption());
                 break;
+            case android.app.job.Flags.FLAG_IGNORE_IMPORTANT_WHILE_FOREGROUND:
+                pw.println(android.app.job.Flags.ignoreImportantWhileForeground());
+                break;
             default:
                 pw.println("Unknown flag: " + flagName);
                 break;
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 ee246d8..790fc6b 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
@@ -129,8 +129,8 @@
     private static final String[] VERB_STRINGS = {
             "VERB_BINDING", "VERB_STARTING", "VERB_EXECUTING", "VERB_STOPPING", "VERB_FINISHED"
     };
-    private static final String TRACE_JOB_FORCE_FINISHED_PREFIX = "forceJobFinished:";
-    private static final String TRACE_JOB_FORCE_FINISHED_DELIMITER = "#";
+    private static final String TRACE_ABANDONED_JOB = "abandonedJob:";
+    private static final String TRACE_ABANDONED_JOB_DELIMITER = "#";
 
     // States that a job occupies while interacting with the client.
     static final int VERB_BINDING = 0;
@@ -294,8 +294,8 @@
         }
 
         @Override
-        public void forceJobFinished(int jobId) {
-            doForceJobFinished(this, jobId);
+        public void handleAbandonedJob(int jobId) {
+            doHandleAbandonedJob(this, jobId);
         }
 
         @Override
@@ -773,7 +773,7 @@
      * This method just adds traces to evaluate jobs that leak jobparameters at the client.
      * It does not stop the job.
      */
-    void doForceJobFinished(JobCallback cb, int jobId) {
+    void doHandleAbandonedJob(JobCallback cb, int jobId) {
         final long ident = Binder.clearCallingIdentity();
         try {
             final JobStatus executing;
@@ -787,9 +787,9 @@
             }
             if (executing != null && jobId == executing.getJobId()) {
                 final StringBuilder stateSuffix = new StringBuilder();
-                stateSuffix.append(TRACE_JOB_FORCE_FINISHED_PREFIX);
+                stateSuffix.append(TRACE_ABANDONED_JOB);
                 stateSuffix.append(executing.getBatteryName());
-                stateSuffix.append(TRACE_JOB_FORCE_FINISHED_DELIMITER);
+                stateSuffix.append(TRACE_ABANDONED_JOB_DELIMITER);
                 stateSuffix.append(executing.getJobId());
                 Trace.instant(Trace.TRACE_TAG_POWER, stateSuffix.toString());
             }
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/BackgroundJobsController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/BackgroundJobsController.java
index e3ac780..7a21697 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/BackgroundJobsController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/BackgroundJobsController.java
@@ -84,9 +84,13 @@
     private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
+            final String action = intent.getAction();
+            if (action == null) {
+                return;
+            }
+
             final String pkgName = getPackageName(intent);
             final int pkgUid = intent.getIntExtra(Intent.EXTRA_UID, -1);
-            final String action = intent.getAction();
             if (pkgUid == -1) {
                 Slog.e(TAG, "Didn't get package UID in intent (" + action + ")");
                 return;
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/DeviceIdleJobsController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/DeviceIdleJobsController.java
index d5c9ae6..abec170 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/DeviceIdleJobsController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/DeviceIdleJobsController.java
@@ -210,7 +210,9 @@
     }
 
     private boolean updateTaskStateLocked(JobStatus task, final long nowElapsed) {
-        final boolean allowInIdle = ((task.getFlags()&JobInfo.FLAG_IMPORTANT_WHILE_FOREGROUND) != 0)
+        final boolean allowInIdle =
+                (!android.app.job.Flags.ignoreImportantWhileForeground()
+                        && ((task.getFlags() & JobInfo.FLAG_IMPORTANT_WHILE_FOREGROUND) != 0))
                 && (mForegroundUids.get(task.getSourceUid()) || isTempWhitelistedLocked(task));
         final boolean whitelisted = isWhitelistedLocked(task);
         final boolean enableTask = !mDeviceIdleMode || whitelisted || allowInIdle;
@@ -219,7 +221,8 @@
 
     @Override
     public void maybeStartTrackingJobLocked(JobStatus jobStatus, JobStatus lastJob) {
-        if ((jobStatus.getFlags()&JobInfo.FLAG_IMPORTANT_WHILE_FOREGROUND) != 0) {
+        if (!android.app.job.Flags.ignoreImportantWhileForeground()
+                && (jobStatus.getFlags() & JobInfo.FLAG_IMPORTANT_WHILE_FOREGROUND) != 0) {
             mAllowInIdleJobs.add(jobStatus);
         }
         updateTaskStateLocked(jobStatus, sElapsedRealtimeClock.millis());
@@ -227,7 +230,8 @@
 
     @Override
     public void maybeStopTrackingJobLocked(JobStatus jobStatus, JobStatus incomingJob) {
-        if ((jobStatus.getFlags()&JobInfo.FLAG_IMPORTANT_WHILE_FOREGROUND) != 0) {
+        if (!android.app.job.Flags.ignoreImportantWhileForeground()
+                && (jobStatus.getFlags() & JobInfo.FLAG_IMPORTANT_WHILE_FOREGROUND) != 0) {
             mAllowInIdleJobs.remove(jobStatus);
         }
     }
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 37e2fe2..ff4af69 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
@@ -793,7 +793,9 @@
                     || isTopStartedJobLocked(jobStatus)
                     || isUidInForeground(jobStatus.getSourceUid());
             final boolean isJobImportant = jobStatus.getEffectivePriority() >= JobInfo.PRIORITY_HIGH
-                    || (jobStatus.getFlags() & JobInfo.FLAG_IMPORTANT_WHILE_FOREGROUND) != 0;
+                    || (!android.app.job.Flags.ignoreImportantWhileForeground()
+                            && (jobStatus.getFlags()
+                                    & JobInfo.FLAG_IMPORTANT_WHILE_FOREGROUND) != 0);
             if (isInPrivilegedState && isJobImportant) {
                 return mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS;
             }
diff --git a/api/Android.bp b/api/Android.bp
index 3c92cb2..ff674c7 100644
--- a/api/Android.bp
+++ b/api/Android.bp
@@ -101,7 +101,9 @@
         "true": [
             "framework-crashrecovery",
         ],
-        default: [],
+        default: [
+            "framework-platformcrashrecovery",
+        ],
     }) + select(release_flag("RELEASE_RANGING_STACK"), {
         true: [
             "framework-ranging",
@@ -436,6 +438,7 @@
     impl_library_visibility: ["//frameworks/base"],
     defaults_visibility: [
         "//frameworks/base/location",
+        "//frameworks/base/packages/CrashRecovery/framework",
         "//frameworks/base/nfc",
     ],
     plugins: ["error_prone_android_framework"],
diff --git a/api/api.go b/api/api.go
index 29083df..f32bdc3 100644
--- a/api/api.go
+++ b/api/api.go
@@ -28,6 +28,7 @@
 const i18n = "i18n.module.public.api"
 const virtualization = "framework-virtualization"
 const location = "framework-location"
+const platformCrashrecovery = "framework-platformcrashrecovery"
 
 var core_libraries_modules = []string{art, conscrypt, i18n}
 
@@ -39,7 +40,7 @@
 // APIs.
 // In addition, the modules in this list are allowed to contribute to test APIs
 // stubs.
-var non_updatable_modules = []string{virtualization, location}
+var non_updatable_modules = []string{virtualization, location, platformCrashrecovery}
 
 // The intention behind this soong plugin is to generate a number of "merged"
 // API-related modules that would otherwise require a large amount of very
diff --git a/api/api_test.go b/api/api_test.go
index 166f053..28109b5e 100644
--- a/api/api_test.go
+++ b/api/api_test.go
@@ -78,10 +78,7 @@
 		"stub-annotations",
 	}
 
-	extraSdkLibraryModules := []string{
-		"framework-virtualization",
-		"framework-location",
-	}
+	extraSdkLibraryModules := non_updatable_modules
 
 	extraSystemModules := []string{
 		"core-public-stubs-system-modules",
@@ -184,10 +181,10 @@
 
 func TestCombinedApisDefaults(t *testing.T) {
 
+	testNonUpdatableModules := append(non_updatable_modules, "framework-foo", "framework-bar")
 	result := android.GroupFixturePreparers(
 		prepareForTestWithCombinedApis,
-		java.FixtureWithLastReleaseApis(
-			"framework-location", "framework-virtualization", "framework-foo", "framework-bar"),
+		java.FixtureWithLastReleaseApis(testNonUpdatableModules...),
 		android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
 			variables.VendorVars = map[string]map[string]string{
 				"boolean_var": {
diff --git a/core/api/current.txt b/core/api/current.txt
index c6d8ff5..bebf98a 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -9113,7 +9113,7 @@
     method public long getTriggerContentUpdateDelay();
     method @Nullable public android.app.job.JobInfo.TriggerContentUri[] getTriggerContentUris();
     method public boolean isExpedited();
-    method public boolean isImportantWhileForeground();
+    method @Deprecated @FlaggedApi("android.app.job.ignore_important_while_foreground") public boolean isImportantWhileForeground();
     method public boolean isPeriodic();
     method public boolean isPersisted();
     method public boolean isPrefetch();
@@ -9218,6 +9218,7 @@
     field public static final int STOP_REASON_QUOTA = 10; // 0xa
     field public static final int STOP_REASON_SYSTEM_PROCESSING = 14; // 0xe
     field public static final int STOP_REASON_TIMEOUT = 3; // 0x3
+    field @FlaggedApi("android.app.job.handle_abandoned_jobs") public static final int STOP_REASON_TIMEOUT_ABANDONED = 16; // 0x10
     field public static final int STOP_REASON_UNDEFINED = 0; // 0x0
     field public static final int STOP_REASON_USER = 13; // 0xd
   }
@@ -9664,6 +9665,47 @@
 
 }
 
+package android.app.wallpaper {
+
+  @FlaggedApi("android.app.live_wallpaper_content_handling") public final class WallpaperDescription implements android.os.Parcelable {
+    method public int describeContents();
+    method @Nullable public android.content.ComponentName getComponent();
+    method @NonNull public android.os.PersistableBundle getContent();
+    method @Nullable public CharSequence getContextDescription();
+    method @Nullable public android.net.Uri getContextUri();
+    method @NonNull public java.util.List<java.lang.CharSequence> getDescription();
+    method @Nullable public String getId();
+    method @Nullable public android.net.Uri getThumbnail();
+    method @Nullable public CharSequence getTitle();
+    method @NonNull public android.app.wallpaper.WallpaperDescription.Builder toBuilder();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @Nullable public static final android.os.Parcelable.Creator<android.app.wallpaper.WallpaperDescription> CREATOR;
+  }
+
+  public static final class WallpaperDescription.Builder {
+    ctor public WallpaperDescription.Builder();
+    method @NonNull public android.app.wallpaper.WallpaperDescription build();
+    method @NonNull public android.app.wallpaper.WallpaperDescription.Builder setContent(@NonNull android.os.PersistableBundle);
+    method @NonNull public android.app.wallpaper.WallpaperDescription.Builder setContextDescription(@Nullable CharSequence);
+    method @NonNull public android.app.wallpaper.WallpaperDescription.Builder setContextUri(@Nullable android.net.Uri);
+    method @NonNull public android.app.wallpaper.WallpaperDescription.Builder setDescription(@NonNull java.util.List<java.lang.CharSequence>);
+    method @NonNull public android.app.wallpaper.WallpaperDescription.Builder setId(@Nullable String);
+    method @NonNull public android.app.wallpaper.WallpaperDescription.Builder setThumbnail(@Nullable android.net.Uri);
+    method @NonNull public android.app.wallpaper.WallpaperDescription.Builder setTitle(@Nullable CharSequence);
+  }
+
+  @FlaggedApi("android.app.live_wallpaper_content_handling") public final class WallpaperInstance implements android.os.Parcelable {
+    ctor public WallpaperInstance(@Nullable android.app.WallpaperInfo, @NonNull android.app.wallpaper.WallpaperDescription);
+    method public int describeContents();
+    method @NonNull public android.app.wallpaper.WallpaperDescription getDescription();
+    method @NonNull public String getId();
+    method @Nullable public android.app.WallpaperInfo getInfo();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.app.wallpaper.WallpaperInstance> CREATOR;
+  }
+
+}
+
 package android.appwidget {
 
   public class AppWidgetHost {
@@ -10872,6 +10914,7 @@
     field public static final String MEDIA_COMMUNICATION_SERVICE = "media_communication";
     field public static final String MEDIA_METRICS_SERVICE = "media_metrics";
     field public static final String MEDIA_PROJECTION_SERVICE = "media_projection";
+    field @FlaggedApi("android.media.tv.flags.media_quality_fw") public static final String MEDIA_QUALITY_SERVICE = "media_quality";
     field public static final String MEDIA_ROUTER_SERVICE = "media_router";
     field public static final String MEDIA_SESSION_SERVICE = "media_session";
     field public static final String MIDI_SERVICE = "midi";
@@ -11485,10 +11528,10 @@
     field public static final String EXTRA_CHANGED_COMPONENT_NAME_LIST = "android.intent.extra.changed_component_name_list";
     field public static final String EXTRA_CHANGED_PACKAGE_LIST = "android.intent.extra.changed_package_list";
     field public static final String EXTRA_CHANGED_UID_LIST = "android.intent.extra.changed_uid_list";
-    field @FlaggedApi("android.service.chooser.chooser_payload_toggling") public static final String EXTRA_CHOOSER_ADDITIONAL_CONTENT_URI = "android.intent.extra.CHOOSER_ADDITIONAL_CONTENT_URI";
+    field public static final String EXTRA_CHOOSER_ADDITIONAL_CONTENT_URI = "android.intent.extra.CHOOSER_ADDITIONAL_CONTENT_URI";
     field public static final String EXTRA_CHOOSER_CONTENT_TYPE_HINT = "android.intent.extra.CHOOSER_CONTENT_TYPE_HINT";
     field public static final String EXTRA_CHOOSER_CUSTOM_ACTIONS = "android.intent.extra.CHOOSER_CUSTOM_ACTIONS";
-    field @FlaggedApi("android.service.chooser.chooser_payload_toggling") public static final String EXTRA_CHOOSER_FOCUSED_ITEM_POSITION = "android.intent.extra.CHOOSER_FOCUSED_ITEM_POSITION";
+    field public static final String EXTRA_CHOOSER_FOCUSED_ITEM_POSITION = "android.intent.extra.CHOOSER_FOCUSED_ITEM_POSITION";
     field public static final String EXTRA_CHOOSER_MODIFY_SHARE_ACTION = "android.intent.extra.CHOOSER_MODIFY_SHARE_ACTION";
     field public static final String EXTRA_CHOOSER_REFINEMENT_INTENT_SENDER = "android.intent.extra.CHOOSER_REFINEMENT_INTENT_SENDER";
     field public static final String EXTRA_CHOOSER_RESULT = "android.intent.extra.CHOOSER_RESULT";
@@ -19034,7 +19077,9 @@
     method @FlaggedApi("android.hardware.biometrics.last_authentication_time") @RequiresPermission(android.Manifest.permission.USE_BIOMETRIC) public long getLastAuthenticationTime(int);
     method @NonNull @RequiresPermission(android.Manifest.permission.USE_BIOMETRIC) public android.hardware.biometrics.BiometricManager.Strings getStrings(int);
     field public static final int BIOMETRIC_ERROR_HW_UNAVAILABLE = 1; // 0x1
+    field @FlaggedApi("android.hardware.biometrics.identity_check_api") public static final int BIOMETRIC_ERROR_IDENTITY_CHECK_NOT_ACTIVE = 20; // 0x14
     field public static final int BIOMETRIC_ERROR_NONE_ENROLLED = 11; // 0xb
+    field @FlaggedApi("android.hardware.biometrics.identity_check_api") public static final int BIOMETRIC_ERROR_NOT_ENABLED_FOR_APPS = 21; // 0x15
     field public static final int BIOMETRIC_ERROR_NO_HARDWARE = 12; // 0xc
     field public static final int BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED = 15; // 0xf
     field @FlaggedApi("android.hardware.biometrics.last_authentication_time") public static final long BIOMETRIC_NO_AUTHENTICATION = -1L; // 0xffffffffffffffffL
@@ -19045,6 +19090,7 @@
     field public static final int BIOMETRIC_STRONG = 15; // 0xf
     field public static final int BIOMETRIC_WEAK = 255; // 0xff
     field public static final int DEVICE_CREDENTIAL = 32768; // 0x8000
+    field @FlaggedApi("android.hardware.biometrics.identity_check_api") public static final int IDENTITY_CHECK = 65536; // 0x10000
   }
 
   public static class BiometricManager.Strings {
@@ -19077,8 +19123,10 @@
     field public static final int BIOMETRIC_ERROR_CANCELED = 5; // 0x5
     field public static final int BIOMETRIC_ERROR_HW_NOT_PRESENT = 12; // 0xc
     field public static final int BIOMETRIC_ERROR_HW_UNAVAILABLE = 1; // 0x1
+    field @FlaggedApi("android.hardware.biometrics.identity_check_api") public static final int BIOMETRIC_ERROR_IDENTITY_CHECK_NOT_ACTIVE = 20; // 0x14
     field public static final int BIOMETRIC_ERROR_LOCKOUT = 7; // 0x7
     field public static final int BIOMETRIC_ERROR_LOCKOUT_PERMANENT = 9; // 0x9
+    field @FlaggedApi("android.hardware.biometrics.identity_check_api") public static final int BIOMETRIC_ERROR_NOT_ENABLED_FOR_APPS = 21; // 0x15
     field public static final int BIOMETRIC_ERROR_NO_BIOMETRICS = 11; // 0xb
     field public static final int BIOMETRIC_ERROR_NO_DEVICE_CREDENTIAL = 14; // 0xe
     field public static final int BIOMETRIC_ERROR_NO_SPACE = 4; // 0x4
@@ -20277,6 +20325,7 @@
     method public long getDynamicRangeProfile();
     method public int getMaxSharedSurfaceCount();
     method public int getMirrorMode();
+    method @FlaggedApi("com.android.internal.camera.flags.mirror_mode_shared_surfaces") public int getMirrorMode(@NonNull android.view.Surface);
     method public long getStreamUseCase();
     method @Nullable public android.view.Surface getSurface();
     method public int getSurfaceGroupId();
@@ -20287,6 +20336,7 @@
     method public void removeSurface(@NonNull android.view.Surface);
     method public void setDynamicRangeProfile(long);
     method public void setMirrorMode(int);
+    method @FlaggedApi("com.android.internal.camera.flags.mirror_mode_shared_surfaces") public void setMirrorMode(@NonNull android.view.Surface, int);
     method public void setPhysicalCameraId(@Nullable String);
     method public void setReadoutTimestampEnabled(boolean);
     method public void setStreamUseCase(long);
@@ -32852,6 +32902,7 @@
 
   public static class Build.VERSION_CODES {
     ctor public Build.VERSION_CODES();
+    field @FlaggedApi("android.sdk.major_minor_versioning_scheme") public static final int BAKLAVA = 10000; // 0x2710
     field public static final int BASE = 1; // 0x1
     field public static final int BASE_1_1 = 2; // 0x2
     field public static final int CUPCAKE = 3; // 0x3
@@ -32891,6 +32942,7 @@
   }
 
   @FlaggedApi("android.sdk.major_minor_versioning_scheme") public static class Build.VERSION_CODES_FULL {
+    field public static final int BAKLAVA = 1000000000; // 0x3b9aca00
     field public static final int BASE = 100000; // 0x186a0
     field public static final int BASE_1_1 = 200000; // 0x30d40
     field public static final int CUPCAKE = 300000; // 0x493e0
@@ -34003,9 +34055,14 @@
   @FlaggedApi("android.os.binder_frozen_state_change_callback") public static final class RemoteCallbackList.Builder<E extends android.os.IInterface> {
     ctor public RemoteCallbackList.Builder(int);
     method @NonNull public android.os.RemoteCallbackList<E> build();
+    method @NonNull public android.os.RemoteCallbackList.Builder setInterfaceDiedCallback(@NonNull android.os.RemoteCallbackList.Builder.InterfaceDiedCallback<E>);
     method @NonNull public android.os.RemoteCallbackList.Builder setMaxQueueSize(int);
   }
 
+  @FlaggedApi("android.os.binder_frozen_state_change_callback") public static interface RemoteCallbackList.Builder.InterfaceDiedCallback<E extends android.os.IInterface> {
+    method public void onInterfaceDied(@NonNull android.os.RemoteCallbackList<E>, E, @Nullable Object);
+  }
+
   public class RemoteException extends android.util.AndroidException {
     ctor public RemoteException();
     ctor public RemoteException(String);
@@ -34122,6 +34179,7 @@
     method public android.os.StrictMode.VmPolicy build();
     method @NonNull public android.os.StrictMode.VmPolicy.Builder detectActivityLeaks();
     method @NonNull public android.os.StrictMode.VmPolicy.Builder detectAll();
+    method @FlaggedApi("com.android.window.flags.bal_strict_mode") @NonNull public android.os.StrictMode.VmPolicy.Builder detectBlockedBackgroundActivityLaunch();
     method @NonNull public android.os.StrictMode.VmPolicy.Builder detectCleartextNetwork();
     method @NonNull public android.os.StrictMode.VmPolicy.Builder detectContentUriWithoutPermission();
     method @NonNull public android.os.StrictMode.VmPolicy.Builder detectCredentialProtectedWhileLocked();
@@ -34134,6 +34192,7 @@
     method @NonNull public android.os.StrictMode.VmPolicy.Builder detectNonSdkApiUsage();
     method @NonNull public android.os.StrictMode.VmPolicy.Builder detectUnsafeIntentLaunch();
     method @NonNull public android.os.StrictMode.VmPolicy.Builder detectUntaggedSockets();
+    method @FlaggedApi("com.android.window.flags.bal_strict_mode") @NonNull public android.os.StrictMode.VmPolicy.Builder ignoreBlockedBackgroundActivityLaunch();
     method @NonNull public android.os.StrictMode.VmPolicy.Builder penaltyDeath();
     method @NonNull public android.os.StrictMode.VmPolicy.Builder penaltyDeathOnCleartextNetwork();
     method @NonNull public android.os.StrictMode.VmPolicy.Builder penaltyDeathOnFileUriExposure();
@@ -34369,10 +34428,13 @@
   public abstract class VibrationEffect implements android.os.Parcelable {
     method public static android.os.VibrationEffect createOneShot(long, int);
     method @NonNull public static android.os.VibrationEffect createPredefined(int);
+    method @FlaggedApi("android.os.vibrator.normalized_pwle_effects") @NonNull public static android.os.VibrationEffect createRepeatingEffect(@NonNull android.os.VibrationEffect);
+    method @FlaggedApi("android.os.vibrator.normalized_pwle_effects") @NonNull public static android.os.VibrationEffect createRepeatingEffect(@NonNull android.os.VibrationEffect, @NonNull android.os.VibrationEffect);
     method public static android.os.VibrationEffect createWaveform(long[], int);
     method public static android.os.VibrationEffect createWaveform(long[], int[], int);
     method public int describeContents();
     method @NonNull public static android.os.VibrationEffect.Composition startComposition();
+    method @FlaggedApi("android.os.vibrator.normalized_pwle_effects") @NonNull public static android.os.VibrationEffect.WaveformEnvelopeBuilder startWaveformEnvelope();
     field @NonNull public static final android.os.Parcelable.Creator<android.os.VibrationEffect> CREATOR;
     field public static final int DEFAULT_AMPLITUDE = -1; // 0xffffffff
     field public static final int EFFECT_CLICK = 0; // 0x0
@@ -34396,6 +34458,11 @@
     field public static final int PRIMITIVE_TICK = 7; // 0x7
   }
 
+  @FlaggedApi("android.os.vibrator.normalized_pwle_effects") public static final class VibrationEffect.WaveformEnvelopeBuilder {
+    method @FlaggedApi("android.os.vibrator.normalized_pwle_effects") @NonNull public android.os.VibrationEffect.WaveformEnvelopeBuilder addControlPoint(@FloatRange(from=0, to=1) float, @FloatRange(from=0) float, int);
+    method @FlaggedApi("android.os.vibrator.normalized_pwle_effects") @NonNull public android.os.VibrationEffect build();
+  }
+
   public abstract class Vibrator {
     method public final int areAllEffectsSupported(@NonNull int...);
     method public final boolean areAllPrimitivesSupported(@NonNull int...);
@@ -40738,7 +40805,7 @@
 
 package android.service.chooser {
 
-  @FlaggedApi("android.service.chooser.chooser_payload_toggling") public interface AdditionalContentContract {
+  public interface AdditionalContentContract {
   }
 
   public static interface AdditionalContentContract.Columns {
@@ -52592,6 +52659,7 @@
     method @NonNull public android.view.SurfaceControl.Transaction setExtendedRangeBrightness(@NonNull android.view.SurfaceControl, float, float);
     method @NonNull public android.view.SurfaceControl.Transaction setFrameRate(@NonNull android.view.SurfaceControl, @FloatRange(from=0.0) float, int);
     method @NonNull public android.view.SurfaceControl.Transaction setFrameRate(@NonNull android.view.SurfaceControl, @FloatRange(from=0.0) float, int, int);
+    method @FlaggedApi("com.android.graphics.surfaceflinger.flags.arr_surfacecontrol_setframerate_api") @NonNull public android.view.SurfaceControl.Transaction setFrameRate(@NonNull android.view.SurfaceControl, @NonNull android.view.Surface.FrameRateParams);
     method @FlaggedApi("com.android.window.flags.sdk_desired_present_time") @NonNull public android.view.SurfaceControl.Transaction setFrameTimeline(long);
     method @Deprecated @NonNull public android.view.SurfaceControl.Transaction setGeometry(@NonNull android.view.SurfaceControl, @Nullable android.graphics.Rect, @Nullable android.graphics.Rect, int);
     method @NonNull public android.view.SurfaceControl.Transaction setLayer(@NonNull android.view.SurfaceControl, @IntRange(from=java.lang.Integer.MIN_VALUE, to=java.lang.Integer.MAX_VALUE) int);
@@ -55040,6 +55108,7 @@
     field public static final int CONTENT_CHANGE_TYPE_DRAG_STARTED = 128; // 0x80
     field public static final int CONTENT_CHANGE_TYPE_ENABLED = 4096; // 0x1000
     field public static final int CONTENT_CHANGE_TYPE_ERROR = 2048; // 0x800
+    field @FlaggedApi("android.view.accessibility.a11y_expansion_state_api") public static final int CONTENT_CHANGE_TYPE_EXPANDED = 16384; // 0x4000
     field public static final int CONTENT_CHANGE_TYPE_PANE_APPEARED = 16; // 0x10
     field public static final int CONTENT_CHANGE_TYPE_PANE_DISAPPEARED = 32; // 0x20
     field public static final int CONTENT_CHANGE_TYPE_PANE_TITLE = 8; // 0x8
@@ -55189,6 +55258,7 @@
     method public CharSequence getContentDescription();
     method public int getDrawingOrder();
     method public CharSequence getError();
+    method @FlaggedApi("android.view.accessibility.a11y_expansion_state_api") public int getExpandedState();
     method @Nullable public android.view.accessibility.AccessibilityNodeInfo.ExtraRenderingInfo getExtraRenderingInfo();
     method public android.os.Bundle getExtras();
     method public CharSequence getHintText();
@@ -55281,6 +55351,7 @@
     method public void setEditable(boolean);
     method public void setEnabled(boolean);
     method public void setError(CharSequence);
+    method @FlaggedApi("android.view.accessibility.a11y_expansion_state_api") public void setExpandedState(int);
     method public void setFocusable(boolean);
     method public void setFocused(boolean);
     method @FlaggedApi("android.view.accessibility.granular_scrolling") public void setGranularScrollingSupported(boolean);
@@ -55367,6 +55438,10 @@
     field @FlaggedApi("android.view.accessibility.tri_state_checked") public static final int CHECKED_STATE_PARTIAL = 2; // 0x2
     field @FlaggedApi("android.view.accessibility.tri_state_checked") public static final int CHECKED_STATE_TRUE = 1; // 0x1
     field @NonNull public static final android.os.Parcelable.Creator<android.view.accessibility.AccessibilityNodeInfo> CREATOR;
+    field @FlaggedApi("android.view.accessibility.a11y_expansion_state_api") public static final int EXPANDED_STATE_COLLAPSED = 1; // 0x1
+    field @FlaggedApi("android.view.accessibility.a11y_expansion_state_api") public static final int EXPANDED_STATE_FULL = 3; // 0x3
+    field @FlaggedApi("android.view.accessibility.a11y_expansion_state_api") public static final int EXPANDED_STATE_PARTIAL = 2; // 0x2
+    field @FlaggedApi("android.view.accessibility.a11y_expansion_state_api") public static final int EXPANDED_STATE_UNDEFINED = 0; // 0x0
     field public static final String EXTRA_DATA_RENDERING_INFO_KEY = "android.view.accessibility.extra.DATA_RENDERING_INFO_KEY";
     field public static final String EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_LENGTH = "android.view.accessibility.extra.DATA_TEXT_CHARACTER_LOCATION_ARG_LENGTH";
     field public static final int EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_MAX_LENGTH = 20000; // 0x4e20
diff --git a/core/api/lint-baseline.txt b/core/api/lint-baseline.txt
index e6a7ca5..1dcafd1 100644
--- a/core/api/lint-baseline.txt
+++ b/core/api/lint-baseline.txt
@@ -395,6 +395,8 @@
     Method can be invoked with an indexing operator from Kotlin: `set` (this is usually desirable; just make sure it makes sense for this type of object)
 
 
+MissingGetterMatchingBuilder: android.os.RemoteCallbackList.Builder#setInterfaceDiedCallback(android.os.RemoteCallbackList.Builder.InterfaceDiedCallback<E>):
+    android.os.RemoteCallbackList does not declare a `getInterfaceDiedCallback()` method matching method android.os.RemoteCallbackList.Builder.setInterfaceDiedCallback(android.os.RemoteCallbackList.Builder.InterfaceDiedCallback<E>)
 RequiresPermission: android.accounts.AccountManager#getAccountsByTypeAndFeatures(String, String[], android.accounts.AccountManagerCallback<android.accounts.Account[]>, android.os.Handler):
     Method 'getAccountsByTypeAndFeatures' documentation mentions permissions without declaring @RequiresPermission
 RequiresPermission: android.accounts.AccountManager#hasFeatures(android.accounts.Account, String[], android.accounts.AccountManagerCallback<java.lang.Boolean>, android.os.Handler):
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 1fdb698..bba52f5 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -209,6 +209,8 @@
     field public static final String MANAGE_FACTORY_RESET_PROTECTION = "android.permission.MANAGE_FACTORY_RESET_PROTECTION";
     field public static final String MANAGE_GAME_ACTIVITY = "android.permission.MANAGE_GAME_ACTIVITY";
     field public static final String MANAGE_GAME_MODE = "android.permission.MANAGE_GAME_MODE";
+    field @FlaggedApi("android.media.tv.flags.media_quality_fw") public static final String MANAGE_GLOBAL_PICTURE_QUALITY_SERVICE = "android.permission.MANAGE_GLOBAL_PICTURE_QUALITY_SERVICE";
+    field @FlaggedApi("android.media.tv.flags.media_quality_fw") public static final String MANAGE_GLOBAL_SOUND_QUALITY_SERVICE = "android.permission.MANAGE_GLOBAL_SOUND_QUALITY_SERVICE";
     field public static final String MANAGE_HOTWORD_DETECTION = "android.permission.MANAGE_HOTWORD_DETECTION";
     field public static final String MANAGE_IPSEC_TUNNELS = "android.permission.MANAGE_IPSEC_TUNNELS";
     field public static final String MANAGE_LOW_POWER_STANDBY = "android.permission.MANAGE_LOW_POWER_STANDBY";
@@ -3353,7 +3355,14 @@
 
 package android.app.wearable {
 
-  @FlaggedApi("android.app.wearable.enable_data_request_observer_api") public final class WearableSensingDataRequest implements android.os.Parcelable {
+  @FlaggedApi("android.app.wearable.enable_concurrent_wearable_connections") public interface WearableConnection {
+    method @NonNull public android.os.ParcelFileDescriptor getConnection();
+    method @NonNull public android.os.PersistableBundle getMetadata();
+    method public void onConnectionAccepted();
+    method public void onError(int);
+  }
+
+  public final class WearableSensingDataRequest implements android.os.Parcelable {
     method public int describeContents();
     method public int getDataSize();
     method public int getDataType();
@@ -3365,29 +3374,35 @@
     field @NonNull public static final android.os.Parcelable.Creator<android.app.wearable.WearableSensingDataRequest> CREATOR;
   }
 
-  @FlaggedApi("android.app.wearable.enable_data_request_observer_api") public static final class WearableSensingDataRequest.Builder {
+  public static final class WearableSensingDataRequest.Builder {
     ctor public WearableSensingDataRequest.Builder(int);
     method @NonNull public android.app.wearable.WearableSensingDataRequest build();
     method @NonNull public android.app.wearable.WearableSensingDataRequest.Builder setRequestDetails(@NonNull android.os.PersistableBundle);
   }
 
   public class WearableSensingManager {
-    method @FlaggedApi("android.app.wearable.enable_data_request_observer_api") @Nullable public static android.app.wearable.WearableSensingDataRequest getDataRequestFromIntent(@NonNull android.content.Intent);
-    method @FlaggedApi("android.app.wearable.enable_provide_wearable_connection_api") @RequiresPermission(android.Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE) public void provideConnection(@NonNull android.os.ParcelFileDescriptor, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
+    method @FlaggedApi("android.app.wearable.enable_concurrent_wearable_connections") @RequiresPermission(android.Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE) public int getAvailableConnectionCount();
+    method @Nullable public static android.app.wearable.WearableSensingDataRequest getDataRequestFromIntent(@NonNull android.content.Intent);
+    method @RequiresPermission(android.Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE) public void provideConnection(@NonNull android.os.ParcelFileDescriptor, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
+    method @FlaggedApi("android.app.wearable.enable_concurrent_wearable_connections") @RequiresPermission(android.Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE) public void provideConnection(@NonNull android.app.wearable.WearableConnection, @NonNull java.util.concurrent.Executor);
     method @RequiresPermission(android.Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE) public void provideData(@NonNull android.os.PersistableBundle, @Nullable android.os.SharedMemory, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
     method @Deprecated @RequiresPermission(android.Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE) public void provideDataStream(@NonNull android.os.ParcelFileDescriptor, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
-    method @FlaggedApi("android.app.wearable.enable_data_request_observer_api") @RequiresPermission(android.Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE) public void registerDataRequestObserver(int, @NonNull android.app.PendingIntent, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
+    method @FlaggedApi("android.app.wearable.enable_provide_read_only_pfd") @RequiresPermission(android.Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE) public void provideReadOnlyParcelFileDescriptor(@NonNull android.os.ParcelFileDescriptor, @NonNull android.os.PersistableBundle, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
+    method @RequiresPermission(android.Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE) public void registerDataRequestObserver(int, @NonNull android.app.PendingIntent, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
+    method @FlaggedApi("android.app.wearable.enable_concurrent_wearable_connections") @RequiresPermission(android.Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE) public void removeAllConnections();
+    method @FlaggedApi("android.app.wearable.enable_concurrent_wearable_connections") @RequiresPermission(android.Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE) public boolean removeConnection(@NonNull android.app.wearable.WearableConnection);
     method @FlaggedApi("android.app.wearable.enable_hotword_wearable_sensing_api") @RequiresPermission(android.Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE) public void startHotwordRecognition(@Nullable android.content.ComponentName, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
     method @FlaggedApi("android.app.wearable.enable_hotword_wearable_sensing_api") @RequiresPermission(android.Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE) public void stopHotwordRecognition(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
-    method @FlaggedApi("android.app.wearable.enable_data_request_observer_api") @RequiresPermission(android.Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE) public void unregisterDataRequestObserver(int, @NonNull android.app.PendingIntent, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
+    method @RequiresPermission(android.Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE) public void unregisterDataRequestObserver(int, @NonNull android.app.PendingIntent, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
     field public static final int STATUS_ACCESS_DENIED = 5; // 0x5
-    field @FlaggedApi("android.app.wearable.enable_provide_wearable_connection_api") public static final int STATUS_CHANNEL_ERROR = 7; // 0x7
+    field public static final int STATUS_CHANNEL_ERROR = 7; // 0x7
+    field @FlaggedApi("android.app.wearable.enable_concurrent_wearable_connections") public static final int STATUS_MAX_CONCURRENT_CONNECTIONS_EXCEEDED = 9; // 0x9
     field public static final int STATUS_SERVICE_UNAVAILABLE = 3; // 0x3
     field public static final int STATUS_SUCCESS = 1; // 0x1
     field public static final int STATUS_UNKNOWN = 0; // 0x0
     field @Deprecated public static final int STATUS_UNSUPPORTED = 2; // 0x2
-    field @FlaggedApi("android.app.wearable.enable_data_request_observer_api") public static final int STATUS_UNSUPPORTED_DATA_TYPE = 8; // 0x8
-    field @FlaggedApi("android.app.wearable.enable_unsupported_operation_status_code") public static final int STATUS_UNSUPPORTED_OPERATION = 6; // 0x6
+    field public static final int STATUS_UNSUPPORTED_DATA_TYPE = 8; // 0x8
+    field public static final int STATUS_UNSUPPORTED_OPERATION = 6; // 0x6
     field public static final int STATUS_WEARABLE_UNAVAILABLE = 4; // 0x4
   }
 
@@ -4637,7 +4652,7 @@
 
   @FlaggedApi("android.content.pm.verification_service") public final class VerificationSession implements android.os.Parcelable {
     method public int describeContents();
-    method @RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT) public long extendTimeRemaining(long);
+    method public long extendTimeRemaining(long);
     method @NonNull public java.util.List<android.content.pm.SharedLibraryInfo> getDeclaredLibraries();
     method @NonNull public android.os.PersistableBundle getExtensionParams();
     method public int getId();
@@ -4645,12 +4660,12 @@
     method @NonNull public String getPackageName();
     method @NonNull public android.content.pm.SigningInfo getSigningInfo();
     method @NonNull public android.net.Uri getStagedPackageUri();
-    method @RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT) public long getTimeoutTime();
+    method public long getTimeoutTime();
     method public int getVerificationPolicy();
-    method @RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT) public void reportVerificationComplete(@NonNull android.content.pm.verify.pkg.VerificationStatus);
-    method @RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT) public void reportVerificationComplete(@NonNull android.content.pm.verify.pkg.VerificationStatus, @NonNull android.os.PersistableBundle);
-    method @RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT) public void reportVerificationIncomplete(int);
-    method @RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT) public boolean setVerificationPolicy(int);
+    method public void reportVerificationComplete(@NonNull android.content.pm.verify.pkg.VerificationStatus);
+    method public void reportVerificationComplete(@NonNull android.content.pm.verify.pkg.VerificationStatus, @NonNull android.os.PersistableBundle);
+    method public void reportVerificationIncomplete(int);
+    method public boolean setVerificationPolicy(int);
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.verify.pkg.VerificationSession> CREATOR;
     field public static final int VERIFICATION_INCOMPLETE_NETWORK_UNAVAILABLE = 1; // 0x1
@@ -7338,6 +7353,7 @@
     method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void setSupportedSystemUsages(@NonNull int[]);
     method @RequiresPermission(anyOf={android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED, android.Manifest.permission.MODIFY_AUDIO_ROUTING}) public void setVolumeGroupVolumeIndex(int, int, int);
     method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void setVolumeIndexForAttributes(@NonNull android.media.AudioAttributes, int, int);
+    method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void setWiredDeviceConnectionState(@NonNull android.media.AudioDeviceAttributes, int);
     method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean supportsBluetoothVariableLatency();
     method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void unregisterAudioPolicy(@NonNull android.media.audiopolicy.AudioPolicy);
     method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void unregisterAudioPolicyAsync(@NonNull android.media.audiopolicy.AudioPolicy);
@@ -7347,6 +7363,8 @@
     field public static final int AUDIOFOCUS_FLAG_DELAY_OK = 1; // 0x1
     field public static final int AUDIOFOCUS_FLAG_LOCK = 4; // 0x4
     field public static final int AUDIOFOCUS_FLAG_PAUSES_ON_DUCKABLE_LOSS = 2; // 0x2
+    field @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public static final int DEVICE_CONNECTION_STATE_CONNECTED = 1; // 0x1
+    field @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public static final int DEVICE_CONNECTION_STATE_DISCONNECTED = 0; // 0x0
     field public static final int DEVICE_VOLUME_BEHAVIOR_ABSOLUTE = 3; // 0x3
     field public static final int DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_ADJUST_ONLY = 5; // 0x5
     field public static final int DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_MULTI_MODE = 4; // 0x4
@@ -14081,34 +14099,9 @@
 
 }
 
-package android.service.watchdog {
-
-  public abstract class ExplicitHealthCheckService extends android.app.Service {
-    ctor public ExplicitHealthCheckService();
-    method public final void notifyHealthCheckPassed(@NonNull String);
-    method @NonNull public final android.os.IBinder onBind(@NonNull android.content.Intent);
-    method public abstract void onCancelHealthCheck(@NonNull String);
-    method @NonNull public abstract java.util.List<java.lang.String> onGetRequestedPackages();
-    method @NonNull public abstract java.util.List<android.service.watchdog.ExplicitHealthCheckService.PackageConfig> onGetSupportedPackages();
-    method public abstract void onRequestHealthCheck(@NonNull String);
-    field public static final String BIND_PERMISSION = "android.permission.BIND_EXPLICIT_HEALTH_CHECK_SERVICE";
-    field public static final String SERVICE_INTERFACE = "android.service.watchdog.ExplicitHealthCheckService";
-  }
-
-  public static final class ExplicitHealthCheckService.PackageConfig implements android.os.Parcelable {
-    ctor public ExplicitHealthCheckService.PackageConfig(@NonNull String, long);
-    method public int describeContents();
-    method public long getHealthCheckTimeoutMillis();
-    method @NonNull public String getPackageName();
-    method public void writeToParcel(android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.service.watchdog.ExplicitHealthCheckService.PackageConfig> CREATOR;
-  }
-
-}
-
 package android.service.wearable {
 
-  @FlaggedApi("android.app.wearable.enable_data_request_observer_api") public interface WearableSensingDataRequester {
+  public interface WearableSensingDataRequester {
     method public void requestData(@NonNull android.app.wearable.WearableSensingDataRequest, @NonNull java.util.function.Consumer<java.lang.Integer>);
     field public static final int STATUS_OBSERVER_CANCELLED = 2; // 0x2
     field public static final int STATUS_SUCCESS = 1; // 0x1
@@ -14121,11 +14114,13 @@
     ctor public WearableSensingService();
     method @Nullable public final android.os.IBinder onBind(@NonNull android.content.Intent);
     method @BinderThread public abstract void onDataProvided(@NonNull android.os.PersistableBundle, @Nullable android.os.SharedMemory, @NonNull java.util.function.Consumer<java.lang.Integer>);
-    method @FlaggedApi("android.app.wearable.enable_data_request_observer_api") @BinderThread public void onDataRequestObserverRegistered(int, @NonNull String, @NonNull android.service.wearable.WearableSensingDataRequester, @NonNull java.util.function.Consumer<java.lang.Integer>);
-    method @FlaggedApi("android.app.wearable.enable_data_request_observer_api") @BinderThread public void onDataRequestObserverUnregistered(int, @NonNull String, @NonNull android.service.wearable.WearableSensingDataRequester, @NonNull java.util.function.Consumer<java.lang.Integer>);
+    method @BinderThread public void onDataRequestObserverRegistered(int, @NonNull String, @NonNull android.service.wearable.WearableSensingDataRequester, @NonNull java.util.function.Consumer<java.lang.Integer>);
+    method @BinderThread public void onDataRequestObserverUnregistered(int, @NonNull String, @NonNull android.service.wearable.WearableSensingDataRequester, @NonNull java.util.function.Consumer<java.lang.Integer>);
     method @BinderThread public abstract void onDataStreamProvided(@NonNull android.os.ParcelFileDescriptor, @NonNull java.util.function.Consumer<java.lang.Integer>);
     method @BinderThread public abstract void onQueryServiceStatus(@NonNull java.util.Set<java.lang.Integer>, @NonNull String, @NonNull java.util.function.Consumer<android.service.ambientcontext.AmbientContextDetectionServiceStatus>);
-    method @FlaggedApi("android.app.wearable.enable_provide_wearable_connection_api") @BinderThread public void onSecureConnectionProvided(@NonNull android.os.ParcelFileDescriptor, @NonNull java.util.function.Consumer<java.lang.Integer>);
+    method @FlaggedApi("android.app.wearable.enable_provide_read_only_pfd") @BinderThread public void onReadOnlyParcelFileDescriptorProvided(@NonNull android.os.ParcelFileDescriptor, @NonNull android.os.PersistableBundle, @NonNull java.util.function.Consumer<java.lang.Integer>);
+    method @BinderThread public void onSecureConnectionProvided(@NonNull android.os.ParcelFileDescriptor, @NonNull java.util.function.Consumer<java.lang.Integer>);
+    method @FlaggedApi("android.app.wearable.enable_concurrent_wearable_connections") @BinderThread public void onSecureConnectionProvided(@NonNull android.os.ParcelFileDescriptor, @NonNull android.os.PersistableBundle, @NonNull java.util.function.Consumer<java.lang.Integer>);
     method @BinderThread public abstract void onStartDetection(@NonNull android.app.ambientcontext.AmbientContextEventRequest, @NonNull String, @NonNull java.util.function.Consumer<android.service.ambientcontext.AmbientContextDetectionServiceStatus>, @NonNull java.util.function.Consumer<android.service.ambientcontext.AmbientContextDetectionResult>);
     method @FlaggedApi("android.app.wearable.enable_hotword_wearable_sensing_api") @BinderThread public void onStartHotwordRecognition(@NonNull java.util.function.Consumer<android.service.voice.HotwordAudioStream>, @NonNull java.util.function.Consumer<java.lang.Integer>);
     method public abstract void onStopDetection(@NonNull String);
@@ -19006,10 +19001,10 @@
     field public final android.content.pm.Signature[] signatures;
   }
 
-  public final class WebViewUpdateService {
-    method public static android.webkit.WebViewProviderInfo[] getAllWebViewPackages();
-    method public static String getCurrentWebViewPackageName();
-    method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public static android.webkit.WebViewProviderInfo[] getValidWebViewPackages();
+  @Deprecated @FlaggedApi("android.webkit.update_service_ipc_wrapper") public final class WebViewUpdateService {
+    method @Deprecated public static android.webkit.WebViewProviderInfo[] getAllWebViewPackages();
+    method @Deprecated public static String getCurrentWebViewPackageName();
+    method @Deprecated @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public static android.webkit.WebViewProviderInfo[] getValidWebViewPackages();
   }
 
 }
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 79bea01..a0c4fdb 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -2771,6 +2771,17 @@
     field @NonNull public static final android.os.Parcelable.Creator<android.os.vibrator.PrimitiveSegment> CREATOR;
   }
 
+  @FlaggedApi("android.os.vibrator.normalized_pwle_effects") public final class PwleSegment extends android.os.vibrator.VibrationEffectSegment {
+    method public int describeContents();
+    method public long getDuration();
+    method public float getEndAmplitude();
+    method public float getEndFrequencyHz();
+    method public float getStartAmplitude();
+    method public float getStartFrequencyHz();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.os.vibrator.PwleSegment> CREATOR;
+  }
+
   public final class RampSegment extends android.os.vibrator.VibrationEffectSegment {
     method public int describeContents();
     method public long getDuration();
@@ -3290,14 +3301,6 @@
 
 }
 
-package android.service.watchdog {
-
-  public abstract class ExplicitHealthCheckService extends android.app.Service {
-    method public void setCallback(@Nullable android.os.RemoteCallback);
-  }
-
-}
-
 package android.speech {
 
   public abstract class RecognitionService extends android.app.Service {
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 5225174..ca98da7 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -1747,7 +1747,7 @@
             printRow(pw, TWO_COUNT_COLUMNS, "Death Recipients:", binderDeathObjectCount,
                     "WebViews:", webviewInstanceCount);
 
-            if (com.android.libcore.Flags.nativeMetrics()) {
+            if (com.android.libcore.readonly.Flags.nativeMetrics()) {
                 dumpMemInfoNativeAllocations(pw);
             }
 
@@ -7681,7 +7681,9 @@
         });
 
         // Register callback to report native memory metrics post GC cleanup
-        if (Flags.reportPostgcMemoryMetricsReadonly() &&
+        // Note: we do not report memory metrics of isolated processes unless
+        // their native allocations become more significant
+        if (!Process.isIsolated() && Flags.reportPostgcMemoryMetrics() &&
             com.android.libcore.readonly.Flags.postCleanupApis()) {
             VMRuntime.addPostCleanupCallback(new Runnable() {
                 @Override public void run() {
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 2e3d226..56538d9 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -8755,21 +8755,9 @@
      * Do a quick check for whether an application might be able to perform an operation.
      * This is <em>not</em> a security check; you must use {@link #noteOp(String, int, String,
      * String, String)} or {@link #startOp(String, int, String, String, String)} for your actual
-     * security checks, which also ensure that the given uid and package name are consistent. This
-     * function can just be used for a quick check to see if an operation has been disabled for the
-     * application, as an early reject of some work.  This does not modify the time stamp or other
-     * data about the operation.
-     *
-     * <p>Important things this will not do (which you need to ultimate use
-     * {@link #noteOp(String, int, String, String, String)} or
-     * {@link #startOp(String, int, String, String, String)} to cover):</p>
-     * <ul>
-     *     <li>Verifying the uid and package are consistent, so callers can't spoof
-     *     their identity.</li>
-     *     <li>Taking into account the current foreground/background state of the
-     *     app; apps whose mode varies by this state will always be reported
-     *     as {@link #MODE_ALLOWED}.</li>
-     * </ul>
+     * security checks. This function can just be used for a quick check to see if an operation has
+     * been disabled for the application, as an early reject of some work.  This does not modify the
+     * time stamp or other data about the operation.
      *
      * @param op The operation to check.  One of the OPSTR_* constants.
      * @param uid The user id of the application attempting to perform the operation.
diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl
index 0a05144..003104a 100644
--- a/core/java/android/app/IActivityTaskManager.aidl
+++ b/core/java/android/app/IActivityTaskManager.aidl
@@ -359,6 +359,23 @@
             in RemoteCallback navigationObserver, in BackAnimationAdapter adaptor);
 
     /**
+     * registers a callback to be invoked when a background activity launch is aborted.
+     *
+     * @param observer callback to be registered.
+     * @return true if the callback was successfully registered, false otherwise.
+     * @hide
+     */
+    boolean registerBackgroundActivityStartCallback(in IBinder binder);
+
+    /**
+     * unregisters a callback to be invoked when a background activity launch is aborted.
+     *
+     * @param observer callback to be registered.
+     * @hide
+     */
+    void unregisterBackgroundActivityStartCallback(in IBinder binder);
+
+    /**
      * registers a callback to be invoked when the screen is captured.
      *
      * @param observer callback to be registered.
diff --git a/core/java/android/app/IBackgroundActivityLaunchCallback.aidl b/core/java/android/app/IBackgroundActivityLaunchCallback.aidl
new file mode 100644
index 0000000..6dfb518
--- /dev/null
+++ b/core/java/android/app/IBackgroundActivityLaunchCallback.aidl
@@ -0,0 +1,26 @@
+/*
+* Copyright 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.app;
+
+/**
+ * Callback to find out when a background activity launch is aborted.
+ * @hide
+ */
+oneway interface IBackgroundActivityLaunchCallback
+{
+    void onBackgroundActivityLaunchAborted(in String message);
+}
diff --git a/core/java/android/app/IUiAutomationConnection.aidl b/core/java/android/app/IUiAutomationConnection.aidl
old mode 100644
new mode 100755
index 9dcfe89..ec5cf75
--- a/core/java/android/app/IUiAutomationConnection.aidl
+++ b/core/java/android/app/IUiAutomationConnection.aidl
@@ -61,6 +61,8 @@
     oneway void shutdown();
     void executeShellCommandWithStderr(String command, in ParcelFileDescriptor sink,
                 in ParcelFileDescriptor source, in ParcelFileDescriptor stderrSink);
+    void executeShellCommandArrayWithStderr(in String[] command, in ParcelFileDescriptor sink,
+                in ParcelFileDescriptor source, in ParcelFileDescriptor stderrSink);
     List<String> getAdoptedShellPermissions();
     void addOverridePermissionState(int uid, String permission, int result);
     void removeOverridePermissionState(int uid, String permission);
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 64aa705..ca1662e6 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -11823,28 +11823,42 @@
                         sanitizeProgressColor(indeterminateColor,
                                 backgroundColor, defaultProgressColor));
             } else {
-
                 // Ensure segment color contrasts.
                 final List<Segment> segments = new ArrayList<>();
+                int totalLength = 0;
                 for (Segment segment : mProgressSegments) {
-                    segments.add(sanitizeSegment(segment, backgroundColor,
-                            defaultProgressColor));
+                    final int length = segment.getLength();
+                    if (length <= 0) continue;
+
+                    try {
+                        totalLength += Math.addExact(totalLength, length);
+                        segments.add(sanitizeSegment(segment, backgroundColor,
+                                defaultProgressColor));
+                    } catch (ArithmeticException e) {
+                        totalLength = DEFAULT_PROGRESS_MAX;
+                        segments.clear();
+                        break;
+                    }
                 }
 
                 // Create default segment when no segments are provided.
                 if (segments.isEmpty()) {
-                    segments.add(sanitizeSegment(new Segment(100), backgroundColor,
+                    totalLength = DEFAULT_PROGRESS_MAX;
+                    segments.add(sanitizeSegment(new Segment(totalLength), backgroundColor,
                             defaultProgressColor));
                 }
 
                 // Ensure point color contrasts.
                 final List<Point> points = new ArrayList<>();
                 for (Point point : mProgressPoints) {
+                    final int position = point.getPosition();
+                    if (position < 0 || position > totalLength) continue;
+
                     points.add(sanitizePoint(point, backgroundColor, defaultProgressColor));
                 }
 
                 model = new NotificationProgressModel(segments, points,
-                        mProgress, mIsStyledByProgress);
+                        Math.clamp(mProgress, 0, totalLength), mIsStyledByProgress);
             }
             return model;
         }
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index 06bf67c..768b70c 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -16,6 +16,8 @@
 
 package android.app;
 
+import static android.service.notification.Flags.notificationClassification;
+
 import android.annotation.CallbackExecutor;
 import android.annotation.FlaggedApi;
 import android.annotation.IntDef;
@@ -37,6 +39,7 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.PackageManager;
 import android.content.pm.ParceledListSlice;
 import android.content.pm.ShortcutInfo;
 import android.graphics.drawable.Icon;
@@ -752,6 +755,11 @@
         INotificationManager service = getService();
         String pkg = mContext.getPackageName();
 
+        if (notificationClassification()
+                && NotificationChannel.SYSTEM_RESERVED_IDS.contains(notification.getChannelId())) {
+            return;
+        }
+
         try {
             if (localLOGV) Log.v(TAG, pkg + ": notify(" + id + ", " + notification + ")");
             service.enqueueNotificationWithTag(pkg, mContext.getOpPackageName(), tag, id,
@@ -1131,6 +1139,10 @@
      * had before it was deleted.
      */
     public void deleteNotificationChannel(String channelId) {
+        if (notificationClassification()
+                && NotificationChannel.SYSTEM_RESERVED_IDS.contains(channelId)) {
+            return;
+        }
         INotificationManager service = getService();
         try {
             service.deleteNotificationChannel(mContext.getPackageName(), channelId);
@@ -1333,11 +1345,15 @@
      */
     @FlaggedApi(Flags.FLAG_MODES_API)
     public boolean areAutomaticZenRulesUserManaged() {
-        // modes ui is dependent on modes api
-        return Flags.modesApi() && Flags.modesUi();
+        if (Flags.modesApi() && Flags.modesUi()) {
+            PackageManager pm = mContext.getPackageManager();
+            return !pm.hasSystemFeature(PackageManager.FEATURE_WATCH)
+                    && !pm.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE);
+        } else {
+            return false;
+        }
     }
 
-
     /**
      * Returns AutomaticZenRules owned by the caller.
      *
diff --git a/core/java/android/app/PropertyInvalidatedCache.java b/core/java/android/app/PropertyInvalidatedCache.java
index c93a6dd..26bfec3 100644
--- a/core/java/android/app/PropertyInvalidatedCache.java
+++ b/core/java/android/app/PropertyInvalidatedCache.java
@@ -30,16 +30,23 @@
 import android.os.Process;
 import android.os.SystemClock;
 import android.os.SystemProperties;
+import android.util.ArrayMap;
 import android.util.Log;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.os.ApplicationSharedMemory;
 import com.android.internal.os.BackgroundThread;
 
+import dalvik.annotation.optimization.CriticalNative;
+import dalvik.annotation.optimization.FastNative;
+
 import java.io.ByteArrayOutputStream;
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.LinkedHashMap;
@@ -63,6 +70,7 @@
  * @hide
  */
 @TestApi
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
 public class PropertyInvalidatedCache<Query, Result> {
     /**
      * This is a configuration class that customizes a cache instance.
@@ -203,19 +211,14 @@
     };
 
     /**
-     * Verify that the property name conforms to the standard.  Log a warning if this is not true.
-     * Note that this is done once in the cache constructor; it does not have to be very fast.
+     * Verify that the property name conforms to the standard and throw if this is not true.  Note
+     * that this is done only once for a given property name; it does not have to be very fast.
      */
-    private void validateCacheKey(String name) {
-        if (Build.IS_USER) {
-            // Do not bother checking keys in user builds.  The keys will have been tested in
-            // eng/userdebug builds already.
-            return;
-        }
+    private static void throwIfInvalidCacheKey(String name) {
         for (int i = 0; i < sValidKeyPrefix.length; i++) {
             if (name.startsWith(sValidKeyPrefix[i])) return;
         }
-        Log.w(TAG, "invalid cache name: " + name);
+        throw new IllegalArgumentException("invalid cache name: " + name);
     }
 
     /**
@@ -234,7 +237,8 @@
      * reserved values cause the cache to be skipped.
      */
     // This is the initial value of all cache keys.  It is changed when a cache is invalidated.
-    private static final int NONCE_UNSET = 0;
+    @VisibleForTesting
+    static final int NONCE_UNSET = 0;
     // This value is used in two ways.  First, it is used internally to indicate that the cache is
     // disabled for the current query.  Secondly, it is used to globally disable the cache across
     // the entire system.  Once a cache is disabled, there is no way to enable it again.  The
@@ -685,6 +689,77 @@
     }
 
     /**
+     * Manage nonces that are stored in shared memory.
+     */
+    private static final class NonceSharedMem extends NonceHandler {
+        // The shared memory.
+        private volatile NonceStore mStore;
+
+        // The index of the nonce in shared memory.
+        private volatile int mHandle = NonceStore.INVALID_NONCE_INDEX;
+
+        // True if the string has been stored, ever.
+        private volatile boolean mRecorded = false;
+
+        // A short name that is saved in shared memory.  This is the portion of the property name
+        // that follows the prefix.
+        private final String mShortName;
+
+        NonceSharedMem(@NonNull String name, @Nullable String prefix) {
+            super(name);
+            if ((prefix != null) && name.startsWith(prefix)) {
+                mShortName = name.substring(prefix.length());
+            } else {
+                mShortName = name;
+            }
+        }
+
+        // Fetch the nonce from shared memory.  If the shared memory is not available, return
+        // UNSET.  If the shared memory is available but the nonce name is not known (it may not
+        // have been invalidated by the server yet), return UNSET.
+        @Override
+        long getNonceInternal() {
+            if (mHandle == NonceStore.INVALID_NONCE_INDEX) {
+                if (mStore == null) {
+                    mStore = NonceStore.getInstance();
+                    if (mStore == null) {
+                        return NONCE_UNSET;
+                    }
+                }
+                mHandle = mStore.getHandleForName(mShortName);
+                if (mHandle == NonceStore.INVALID_NONCE_INDEX) {
+                    return NONCE_UNSET;
+                }
+            }
+            return mStore.getNonce(mHandle);
+        }
+
+        // Set the nonce in shared mmory.  If the shared memory is not available, throw an
+        // exception.  Otherwise, if the nonce name has never been recorded, record it now and
+        // fetch the handle for the name.  If the handle cannot be created, throw an exception.
+        @Override
+        void setNonceInternal(long value) {
+            if (mHandle == NonceStore.INVALID_NONCE_INDEX || !mRecorded) {
+                if (mStore == null) {
+                    mStore = NonceStore.getInstance();
+                    if (mStore == null) {
+                        throw new IllegalStateException("setNonce: shared memory not ready");
+                    }
+                }
+                // Always store the name before fetching the handle.  storeName() is idempotent
+                // but does take a little time, so this code calls it just once.
+                mStore.storeName(mShortName);
+                mRecorded = true;
+                mHandle = mStore.getHandleForName(mShortName);
+                if (mHandle == NonceStore.INVALID_NONCE_INDEX) {
+                    throw new IllegalStateException("setNonce: shared memory store failed");
+                }
+            }
+            mStore.setNonce(mHandle, value);
+        }
+    }
+
+    /**
      * SystemProperties and shared storage are protected and cannot be written by random
      * processes.  So, for testing purposes, the NonceLocal handler stores the nonce locally.  The
      * NonceLocal uses the mTestNonce in the superclass, regardless of test mode.
@@ -712,6 +787,7 @@
      * Complete key prefixes.
      */
     private static final String PREFIX_TEST = CACHE_KEY_PREFIX + "." + MODULE_TEST + ".";
+    private static final String PREFIX_SYSTEM = CACHE_KEY_PREFIX + "." + MODULE_SYSTEM + ".";
 
     /**
      * A static list of nonce handlers, indexed by name.  NonceHandlers can be safely shared by
@@ -722,16 +798,40 @@
     private static final ConcurrentHashMap<String, NonceHandler> sHandlers
             = new ConcurrentHashMap<>();
 
+    // True if shared memory is flag-enabled, false otherwise.  Read the flags exactly once.
+    private static final boolean sSharedMemoryAvailable = isSharedMemoryAvailable();
+
+    @android.ravenwood.annotation.RavenwoodReplace
+    private static boolean isSharedMemoryAvailable() {
+        return com.android.internal.os.Flags.applicationSharedMemoryEnabled()
+                && android.app.Flags.picUsesSharedMemory();
+    }
+
+    private static boolean isSharedMemoryAvailable$ravenwood() {
+        return false; // Always disable shared memory on Ravenwood. (for now)
+    }
+
+    // Return true if this cache can use shared memory for its nonce.  Shared memory may be used
+    // if the module is the system.
+    private static boolean sharedMemoryOkay(@NonNull String name) {
+        return sSharedMemoryAvailable && name.startsWith(PREFIX_SYSTEM);
+    }
+
     /**
-     * Return the proper nonce handler, based on the property name.
+     * Return the proper nonce handler, based on the property name.  A handler is created if
+     * necessary.  Before a handler is created, the name is checked, and an exception is thrown if
+     * the name is not valid.
      */
     private static NonceHandler getNonceHandler(@NonNull String name) {
         NonceHandler h = sHandlers.get(name);
         if (h == null) {
             synchronized (sGlobalLock) {
+                throwIfInvalidCacheKey(name);
                 h = sHandlers.get(name);
                 if (h == null) {
-                    if (name.startsWith(PREFIX_TEST)) {
+                    if (sharedMemoryOkay(name)) {
+                        h = new NonceSharedMem(name, PREFIX_SYSTEM);
+                    } else if (name.startsWith(PREFIX_TEST)) {
                         h = new NonceLocal(name);
                     } else {
                         h = new NonceSysprop(name);
@@ -774,7 +874,6 @@
     public PropertyInvalidatedCache(int maxEntries, @NonNull String propertyName,
             @NonNull String cacheName) {
         mPropertyName = propertyName;
-        validateCacheKey(mPropertyName);
         mCacheName = cacheName;
         mNonce = getNonceHandler(mPropertyName);
         mMaxEntries = maxEntries;
@@ -799,7 +898,6 @@
     public PropertyInvalidatedCache(int maxEntries, @NonNull String module, @NonNull String api,
             @NonNull String cacheName, @NonNull QueryHandler<Query, Result> computer) {
         mPropertyName = createPropertyName(module, api);
-        validateCacheKey(mPropertyName);
         mCacheName = cacheName;
         mNonce = getNonceHandler(mPropertyName);
         mMaxEntries = maxEntries;
@@ -855,7 +953,12 @@
     public static void setTestMode(boolean mode) {
         synchronized (sGlobalLock) {
             if (sTestMode == mode) {
-                throw new IllegalStateException("cannot set test mode redundantly: mode=" + mode);
+                final String msg = "cannot set test mode redundantly: mode=" + mode;
+                if (Flags.enforcePicTestmodeProtocol()) {
+                    throw new IllegalStateException(msg);
+                } else {
+                    Log.e(TAG, msg);
+                }
             }
             sTestMode = mode;
             if (mode) {
@@ -1620,6 +1723,14 @@
         // then only that cache is reported.
         boolean detail = anyDetailed(args);
 
+        if (sSharedMemoryAvailable) {
+            pw.println("  SharedMemory: enabled");
+            NonceStore.getInstance().dump(pw, "    ", detail);
+        } else {
+            pw.println("  SharedMemory: disabled");
+         }
+        pw.println();
+
         ArrayList<PropertyInvalidatedCache> activeCaches = getActiveCaches();
         for (int i = 0; i < activeCaches.size(); i++) {
             PropertyInvalidatedCache currentCache = activeCaches.get(i);
@@ -1654,4 +1765,363 @@
             Log.e(TAG, "Failed to dump PropertyInvalidatedCache instances");
         }
     }
+
+    /**
+     * Nonces in shared memory are supported by a string block that acts as a table of contents
+     * for nonce names, and an array of nonce values.  There are two key design principles with
+     * respect to nonce maps:
+     *
+     * 1. It is always okay if a nonce value cannot be determined.  If the nonce is UNSET, the
+     * cache is bypassed, which is always functionally correct.  Clients do not take extraordinary
+     * measures to be current with the nonce map.  Clients must be current with the nonce itself;
+     * this is achieved through the shared memory.
+     *
+     * 2. Once a name is mapped to a nonce index, the mapping is fixed for the lifetime of the
+     * system.  It is only necessary to distinguish between the unmapped and mapped states.  Once
+     * a client has mapped a nonce, that mapping is known to be good for the lifetime of the
+     * system.
+     * @hide
+     */
+    @VisibleForTesting
+    public static class NonceStore {
+
+        // A lock for the store.
+        private final Object mLock = new Object();
+
+        // The native pointer.  This is not owned by this class.  It is owned by
+        // ApplicationSharedMemory, and it disappears when the owning instance is closed.
+        private final long mPtr;
+
+        // True if the memory is immutable.
+        private final boolean mMutable;
+
+        // The maximum length of a string in the string block.  The maximum length must fit in a
+        // byte, but a smaller value has been chosen to limit memory use.  Because strings are
+        // run-length encoded, a string consumes at most MAX_STRING_LENGTH+1 bytes in the string
+        // block.
+        private static final int MAX_STRING_LENGTH = 63;
+
+        // The expected hash code of the string block.  If the hash over the string block equals
+        // this value, then the string block is valid.  Otherwise, the block is not valid and
+        // should be re-read.  An invalid block generally means that a client has read the shared
+        // memory while the server was still writing it.
+        @GuardedBy("mLock")
+        private int mBlockHash = 0;
+
+        // The number of nonces that the native layer can hold.  This is maintained for debug and
+        // logging.
+        private final int mMaxNonce;
+
+        // The size of the native byte block.
+        private final int mMaxByte;
+
+        /** @hide */
+        @VisibleForTesting
+        public NonceStore(long ptr, boolean mutable) {
+            mPtr = ptr;
+            mMutable = mutable;
+            mMaxByte = nativeGetMaxByte(ptr);
+            mMaxNonce = nativeGetMaxNonce(ptr);
+            refreshStringBlockLocked();
+        }
+
+        // The static lock for singleton acquisition.
+        private static Object sLock = new Object();
+
+        // NonceStore is supposed to be a singleton.
+        private static NonceStore sInstance;
+
+        // Return the singleton instance.
+        static NonceStore getInstance() {
+            synchronized (sLock) {
+                if (sInstance == null) {
+                    try {
+                        ApplicationSharedMemory shmem = ApplicationSharedMemory.getInstance();
+                        sInstance = (shmem == null)
+                                    ? null
+                                    : new NonceStore(shmem.getSystemNonceBlock(),
+                                            shmem.isMutable());
+                    } catch (IllegalStateException e) {
+                        // ApplicationSharedMemory.getInstance() throws if the shared memory is
+                        // not yet mapped.  Swallow the exception and leave sInstance null.
+                    }
+                }
+                return sInstance;
+            }
+        }
+
+        // The index value of an unmapped name.
+        public static final int INVALID_NONCE_INDEX = -1;
+
+        // The highest string index extracted from the string block.  -1 means no strings have
+        // been seen.  This is used to skip strings that have already been processed, when the
+        // string block is updated.
+        @GuardedBy("mLock")
+        private int mHighestIndex = -1;
+
+        // The number bytes of the string block that has been used.  This is a statistics.
+        @GuardedBy("mLock")
+        private int mStringBytes = 0;
+
+        // The number of partial reads on the string block.  This is a statistic.
+        @GuardedBy("mLock")
+        private int mPartialReads = 0;
+
+        // The number of times the string block was updated.  This is a statistic.
+        @GuardedBy("mLock")
+        private int mStringUpdated = 0;
+
+        // Map a string to a native index.
+        @GuardedBy("mLock")
+        private final ArrayMap<String, Integer> mStringHandle = new ArrayMap<>();
+
+        // Update the string map from the current string block.  The string block is not modified
+        // and the block hash is not checked.  The function skips past strings that have already
+        // been read, and then processes any new strings.
+        @GuardedBy("mLock")
+        private void updateStringMapLocked(byte[] block) {
+            int index = 0;
+            int offset = 0;
+            while (offset < block.length && block[offset] != 0) {
+                if (index > mHighestIndex) {
+                    // Only record the string if it has not been seen yet.
+                    final String s = new String(block, offset+1, block[offset]);
+                    mStringHandle.put(s, index);
+                    mHighestIndex = index;
+                }
+                offset += block[offset] + 1;
+                index++;
+            }
+            mStringBytes = offset;
+        }
+
+        // Append a string to the string block and update the hash.  This does not write the block
+        // to shared memory.
+        @GuardedBy("mLock")
+        private void appendStringToMapLocked(@NonNull String str, @NonNull byte[] block) {
+            int offset = 0;
+            while (offset < block.length && block[offset] != 0) {
+                offset += block[offset] + 1;
+            }
+            final byte[] strBytes = str.getBytes();
+
+            if (offset + strBytes.length >= block.length) {
+                // Overflow.  Do not add the string to the block; the string will remain undefined.
+                return;
+            }
+
+            block[offset] = (byte) strBytes.length;
+            System.arraycopy(strBytes, 0, block, offset+1, strBytes.length);
+            mBlockHash = Arrays.hashCode(block);
+        }
+
+        // Possibly update the string block.  If the native shared memory has a new block hash,
+        // then read the new string block values from shared memory, as well as the new hash.
+        @GuardedBy("mLock")
+        private void refreshStringBlockLocked() {
+            if (mBlockHash == nativeGetByteBlockHash(mPtr)) {
+                // The fastest way to know that the shared memory string block has not changed.
+                return;
+            }
+            byte[] block = new byte[mMaxByte];
+            final int hash = nativeGetByteBlock(mPtr, mBlockHash, block);
+            if (hash != Arrays.hashCode(block)) {
+                // This is a partial read: ignore it.  The next time someone needs this string
+                // the memory will be read again and should succeed.  Set the local hash to
+                // zero to ensure that the next read attempt will actually read from shared
+                // memory.
+                mBlockHash = 0;
+                mPartialReads++;
+                return;
+            }
+            // The hash has changed.  Update the strings from the byte block.
+            mStringUpdated++;
+            mBlockHash = hash;
+            updateStringMapLocked(block);
+        }
+
+        // Throw an exception if the string cannot be stored in the string block.
+        private static void throwIfBadString(@NonNull String s) {
+            if (s.length() == 0) {
+                throw new IllegalArgumentException("cannot store an empty string");
+            }
+            if (s.length() > MAX_STRING_LENGTH) {
+                throw new IllegalArgumentException("cannot store a string longer than "
+                        + MAX_STRING_LENGTH);
+            }
+        }
+
+        // Throw an exception if the nonce handle is invalid.  The handle is bad if it is out of
+        // range of allocated handles.  Note that NONCE_HANDLE_INVALID will throw: this is
+        // important for setNonce().
+        @GuardedBy("mLock")
+        private void throwIfBadHandle(int handle) {
+            if (handle < 0 || handle > mHighestIndex) {
+                throw new IllegalArgumentException("invalid nonce handle: " + handle);
+            }
+        }
+
+        // Throw if the memory is immutable (the process does not have write permission).  The
+        // exception mimics the permission-denied exception thrown when a process writes to an
+        // unauthorized system property.
+        private void throwIfImmutable() {
+            if (!mMutable) {
+                throw new RuntimeException("write permission denied");
+            }
+        }
+
+        static final AtomicLong sStoreCount = new AtomicLong();
+
+
+        // Add a string to the local copy of the block and write the block to shared memory.
+        // Return the index of the new string.  If the string has already been recorded, the
+        // shared memory is not updated but the index of the existing string is returned.
+        public int storeName(@NonNull String str) {
+            synchronized (mLock) {
+                Integer handle = mStringHandle.get(str);
+                if (handle == null) {
+                    throwIfImmutable();
+                    throwIfBadString(str);
+                    byte[] block = new byte[mMaxByte];
+                    nativeGetByteBlock(mPtr, 0, block);
+                    appendStringToMapLocked(str, block);
+                    nativeSetByteBlock(mPtr, mBlockHash, block);
+                    updateStringMapLocked(block);
+                    handle = mStringHandle.get(str);
+                }
+                return handle;
+            }
+        }
+
+        // Retrieve the handle for a string.  -1 is returned if the string is not found.
+        public int getHandleForName(@NonNull String str) {
+            synchronized (mLock) {
+                Integer handle = mStringHandle.get(str);
+                if (handle == null) {
+                    refreshStringBlockLocked();
+                    handle  = mStringHandle.get(str);
+                }
+                return (handle != null) ? handle : INVALID_NONCE_INDEX;
+            }
+        }
+
+        // Thin wrapper around the native method.
+        public boolean setNonce(int handle, long value) {
+            synchronized (mLock) {
+                throwIfBadHandle(handle);
+                throwIfImmutable();
+                return nativeSetNonce(mPtr, handle, value);
+            }
+        }
+
+        public long getNonce(int handle) {
+            synchronized (mLock) {
+                throwIfBadHandle(handle);
+                return nativeGetNonce(mPtr, handle);
+            }
+        }
+
+        /**
+         * Dump the nonce statistics
+         */
+        public void dump(@NonNull PrintWriter pw, @NonNull String prefix, boolean detailed) {
+            synchronized (mLock) {
+                pw.println(formatSimple(
+                    "%sStringsMapped: %d, BytesUsed: %d",
+                    prefix, mHighestIndex, mStringBytes));
+                pw.println(formatSimple(
+                    "%sPartialReads: %d, StringUpdates: %d",
+                    prefix, mPartialReads, mStringUpdated));
+
+                if (detailed) {
+                    for (String s: mStringHandle.keySet()) {
+                        int h = mStringHandle.get(s);
+                        pw.println(formatSimple(
+                            "%sHandle:%d Name:%s", prefix, h, s));
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Return the maximum number of nonces supported in the native layer.
+     *
+     * @param mPtr the pointer to the native shared memory.
+     * @return the number of nonces supported by the shared memory.
+     */
+    @FastNative
+    private static native int nativeGetMaxNonce(long mPtr);
+
+    /**
+     * Return the maximum number of string bytes supported in the native layer.
+     *
+     * @param mPtr the pointer to the native shared memory.
+     * @return the number of string bytes supported by the shared memory.
+     */
+    @FastNative
+    private static native int nativeGetMaxByte(long mPtr);
+
+    /**
+     * Write the byte block and set the hash into shared memory.  The method is relatively
+     * forgiving, in that any non-null byte array will be stored without error.  The number of
+     * bytes will the lesser of the length of the block parameter and the size of the native
+     * array.  The native layer performs no checks on either byte block or the hash.
+     *
+     * @param mPtr the pointer to the native shared memory.
+     * @param hash a value to be stored in the native block hash.
+     * @param block the byte array to be store.
+     */
+    @FastNative
+    private static native void nativeSetByteBlock(long mPtr, int hash, @NonNull byte[] block);
+
+    /**
+     * Retrieve the string block into the array and return the hash value.  If the incoming hash
+     * value is the same as the hash in shared memory, the native function returns immediately
+     * without touching the block parameter.  Note that a zero hash value will always cause shared
+     * memory to be read.  The number of bytes read is the lesser of the length of the block
+     * parameter and the size of the native array.
+     *
+     * @param mPtr the pointer to the native shared memory.
+     * @param hash a value to be compared against the hash in the native layer.
+     * @param block an array to receive the bytes from the native layer.
+     * @return the hash from the native layer.
+     */
+    @FastNative
+    private static native int nativeGetByteBlock(long mPtr, int hash, @NonNull byte[] block);
+
+    /**
+     * Retrieve just the byte block hash from the native layer.  The function is CriticalNative
+     * and thus very fast.
+     *
+     * @param mPtr the pointer to the native shared memory.
+     * @return the current native hash value.
+     */
+    @CriticalNative
+    private static native int nativeGetByteBlockHash(long mPtr);
+
+    /**
+     * Set a nonce at the specified index.  The index is checked against the size of the native
+     * nonce array and the function returns true if the index is valid, and false.  The function
+     * is CriticalNative and thus very fast.
+     *
+     * @param mPtr the pointer to the native shared memory.
+     * @param index the index of the nonce to set.
+     * @param value the value to set for the nonce.
+     * @return true if the index is inside the nonce array and false otherwise.
+     */
+    @CriticalNative
+    private static native boolean nativeSetNonce(long mPtr, int index, long value);
+
+    /**
+     * Get the nonce from the specified index.  The index is checked against the size of the
+     * native nonce array; the function returns the nonce value if the index is valid, and 0
+     * otherwise.  The function is CriticalNative and thus very fast.
+     *
+     * @param mPtr the pointer to the native shared memory.
+     * @param index the index of the nonce to retrieve.
+     * @return the value of the specified nonce, of 0 if the index is out of bounds.
+     */
+    @CriticalNative
+    private static native long nativeGetNonce(long mPtr, int index);
 }
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index bd26db5..e451116 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -150,6 +150,8 @@
 import android.media.musicrecognition.IMusicRecognitionManager;
 import android.media.musicrecognition.MusicRecognitionManager;
 import android.media.projection.MediaProjectionManager;
+import android.media.quality.IMediaQualityManager;
+import android.media.quality.MediaQualityManager;
 import android.media.soundtrigger.SoundTriggerManager;
 import android.media.tv.ITvInputManager;
 import android.media.tv.TvInputManager;
@@ -1750,10 +1752,13 @@
                         @Override
                         public AdvancedProtectionManager createService(ContextImpl ctx)
                                 throws ServiceNotFoundException {
-                            IBinder iBinder = ServiceManager.getServiceOrThrow(
+                            IBinder iBinder = ServiceManager.getService(
                                     Context.ADVANCED_PROTECTION_SERVICE);
                             IAdvancedProtectionService service =
                                     IAdvancedProtectionService.Stub.asInterface(iBinder);
+                            if (service == null) {
+                                return null;
+                            }
                             return new AdvancedProtectionManager(service);
                         }
                     });
@@ -1772,6 +1777,19 @@
                         }
                     });
         }
+        registerService(Context.MEDIA_QUALITY_SERVICE, MediaQualityManager.class,
+                new CachedServiceFetcher<MediaQualityManager>() {
+                    @Override
+                    public MediaQualityManager createService(ContextImpl ctx)
+                            throws ServiceNotFoundException {
+                        IBinder iBinder = ServiceManager
+                                .getServiceOrThrow(Context.MEDIA_QUALITY_SERVICE);
+                        IMediaQualityManager service = IMediaQualityManager
+                                .Stub.asInterface(iBinder);
+                        return new MediaQualityManager(ctx, service);
+                    }
+                });
+
         sInitializing = true;
         try {
             // Note: the following functions need to be @SystemApis, once they become mainline
diff --git a/core/java/android/app/UiAutomationConnection.java b/core/java/android/app/UiAutomationConnection.java
old mode 100644
new mode 100755
index 12f2081..d4bad39
--- a/core/java/android/app/UiAutomationConnection.java
+++ b/core/java/android/app/UiAutomationConnection.java
@@ -565,7 +565,12 @@
             // Rethrow the exception. This will be propagated to the remote caller.
             throw ex;
         }
+        handleExecuteShellCommandProcess(process, sink, source, stderrSink);
+    }
 
+    private void handleExecuteShellCommandProcess(final java.lang.Process process,
+            final ParcelFileDescriptor sink, final ParcelFileDescriptor source,
+            final ParcelFileDescriptor stderrSink) {
         // Read from process and write to pipe
         final Thread readFromProcess;
         if (sink != null) {
@@ -628,6 +633,26 @@
     }
 
     @Override
+    public void executeShellCommandArrayWithStderr(final String[] command,
+            final ParcelFileDescriptor sink, final ParcelFileDescriptor source,
+            final ParcelFileDescriptor stderrSink) throws RemoteException {
+        synchronized (mLock) {
+            throwIfCalledByNotTrustedUidLocked();
+            throwIfShutdownLocked();
+            throwIfNotConnectedLocked();
+        }
+        final java.lang.Process process;
+
+        try {
+            process = Runtime.getRuntime().exec(command);
+        } catch (IOException exc) {
+            throw new RuntimeException(
+                    "Error running shell command '" + String.join(" ", command) + "'", exc);
+        }
+        handleExecuteShellCommandProcess(process, sink, source, stderrSink);
+    }
+
+    @Override
     public void shutdown() {
         synchronized (mLock) {
             if (isConnectedLocked()) {
diff --git a/core/java/android/app/appfunctions/AppFunctionManager.java b/core/java/android/app/appfunctions/AppFunctionManager.java
index dca4336..8944bb9 100644
--- a/core/java/android/app/appfunctions/AppFunctionManager.java
+++ b/core/java/android/app/appfunctions/AppFunctionManager.java
@@ -42,12 +42,38 @@
 import java.util.function.Consumer;
 
 /**
- * Provides app functions related functionalities.
+ * Provides access to app functions.
  *
- * <p>App function is a specific piece of functionality that an app offers to the system. These
- * functionalities can be integrated into various system features.
+ * <p>An app function is a piece of functionality that apps expose to the system for cross-app
+ * orchestration.
+ *
+ * <p>**Building App Functions:**
+ *
+ * <p>Most developers should build app functions through the AppFunctions SDK. This SDK library
+ * offers a more convenient and type-safe way to represent the inputs and outputs of an app
+ * function, using custom data classes called "AppFunction Schemas".
+ *
+ * <p>The suggested way to build an app function is to use the AppFunctions SDK. The SDK provides
+ * custom data classes (AppFunctions Schemas) and handles the conversion to the underlying {@link
+ * android.app.appsearch.GenericDocument}/{@link android.os.Bundle} format used in {@link
+ * ExecuteAppFunctionRequest} and {@link ExecuteAppFunctionResponse}.
+ *
+ * <p>**Discovering (Listing) App Functions:**
+ *
+ * <p>When there is a package change or the device starts up, the metadata of available functions is
+ * indexed on-device by {@link AppSearchManager}. AppSearch stores the indexed information as a
+ * {@code AppFunctionStaticMetadata} document. This allows other apps and the app itself to discover
+ * these functions using the AppSearch search APIs. Visibility to this metadata document is based on
+ * the packages that have visibility to the app providing the app functions.
+ *
+ * <p>**Executing App Functions:**
+ *
+ * <p>Requests to execute a function are built using the {@link ExecuteAppFunctionRequest} class.
+ * Callers need the {@code android.permission.EXECUTE_APP_FUNCTIONS} or {@code
+ * android.permission.EXECUTE_APP_FUNCTIONS_TRUSTED} permission to execute app functions from other
+ * apps. An app has automatic visibility to its own functions and doesn't need these permissions to
+ * call its own functions via {@code AppFunctionManager}.
  */
-// TODO(b/357551503): Implement get and set enabled app function APIs.
 @FlaggedApi(FLAG_ENABLE_APP_FUNCTION_MANAGER)
 @SystemService(Context.APP_FUNCTION_SERVICE)
 public final class AppFunctionManager {
@@ -111,17 +137,19 @@
      * @param request the request to execute the app function
      * @param executor the executor to run the callback
      * @param cancellationSignal the cancellation signal to cancel the execution.
-     * @param callback the callback to receive the function execution result. if the calling app
-     *     does not own the app function or does not have {@code
+     * @param callback the callback to receive the function execution result.
+     *     <p>If the calling app does not own the app function or does not have {@code
      *     android.permission.EXECUTE_APP_FUNCTIONS_TRUSTED} or {@code
      *     android.permission.EXECUTE_APP_FUNCTIONS}, the execution result will contain {@code
      *     ExecuteAppFunctionResponse.RESULT_DENIED}.
+     *     <p>If the caller only has {@code android.permission.EXECUTE_APP_FUNCTIONS} but the
+     *     function requires {@code android.permission.EXECUTE_APP_FUNCTIONS_TRUSTED}, the execution
+     *     result will contain {@code ExecuteAppFunctionResponse.RESULT_DENIED}
+     *     <p>If the function requested for execution is disabled, then the execution result will
+     *     contain {@code ExecuteAppFunctionResponse.RESULT_DISABLED}
+     *     <p>If the cancellation signal is issued, the operation is cancelled and no response is
+     *     returned to the caller.
      */
-    // TODO(b/357551503): Document the behavior when the cancellation signal is issued.
-    // TODO(b/360864791): Document that apps can opt-out from being executed by callers with
-    //   EXECUTE_APP_FUNCTIONS and how a caller knows whether a function is opted out.
-    // TODO(b/357551503): Update documentation when get / set APIs are implemented that this will
-    //   also return RESULT_DENIED if the app function is disabled.
     @RequiresPermission(
             anyOf = {
                 Manifest.permission.EXECUTE_APP_FUNCTIONS_TRUSTED,
diff --git a/core/java/android/app/appfunctions/ExecuteAppFunctionRequest.java b/core/java/android/app/appfunctions/ExecuteAppFunctionRequest.java
index fe7fd88..4c5e8c1 100644
--- a/core/java/android/app/appfunctions/ExecuteAppFunctionRequest.java
+++ b/core/java/android/app/appfunctions/ExecuteAppFunctionRequest.java
@@ -50,12 +50,12 @@
                 }
             };
 
-    /** Returns the package name of the app that hosts the function. */
+    /** Returns the package name of the app that hosts/owns the function. */
     @NonNull private final String mTargetPackageName;
 
     /**
-     * Returns the unique string identifier of the app function to be executed. TODO(b/357551503):
-     * Document how callers can get the available function identifiers.
+     * The unique string identifier of the app function to be executed. This identifier is used to
+     * execute a specific app function.
      */
     @NonNull private final String mFunctionIdentifier;
 
@@ -69,8 +69,6 @@
      *
      * <p>The document may have missing parameters. Developers are advised to implement defensive
      * handling measures.
-     *
-     * <p>TODO(b/357551503): Document how function parameters can be obtained for function execution
      */
     @NonNull private final GenericDocumentWrapper mParameters;
 
@@ -91,7 +89,19 @@
         return mTargetPackageName;
     }
 
-    /** Returns the unique string identifier of the app function to be executed. */
+    /**
+     * Returns the unique string identifier of the app function to be executed.
+     *
+     * <p>When there is a package change or the device starts up, the metadata of available
+     * functions is indexed by AppSearch. AppSearch stores the indexed information as {@code
+     * AppFunctionStaticMetadata} document.
+     *
+     * <p>The ID can be obtained by querying the {@code AppFunctionStaticMetadata} documents from
+     * AppSearch.
+     *
+     * <p>If the {@code functionId} provided is invalid, the caller will get an invalid argument
+     * response.
+     */
     @NonNull
     public String getFunctionIdentifier() {
         return mFunctionIdentifier;
@@ -103,6 +113,12 @@
      *
      * <p>The bundle may have missing parameters. Developers are advised to implement defensive
      * handling measures.
+     *
+     * <p>Similar to {@link #getFunctionIdentifier()} the parameters required by a function can be
+     * obtained by querying AppSearch for the corresponding {@code AppFunctionStaticMetadata}. This
+     * metadata will contain enough information for the caller to resolve the required parameters
+     * either using information from the metadata itself or using the AppFunction SDK for function
+     * callers.
      */
     @NonNull
     public GenericDocument getParameters() {
diff --git a/core/java/android/app/appfunctions/ExecuteAppFunctionResponse.java b/core/java/android/app/appfunctions/ExecuteAppFunctionResponse.java
index a879b1b..c907ef1 100644
--- a/core/java/android/app/appfunctions/ExecuteAppFunctionResponse.java
+++ b/core/java/android/app/appfunctions/ExecuteAppFunctionResponse.java
@@ -79,14 +79,15 @@
     /** The caller does not have the permission to execute an app function. */
     public static final int RESULT_DENIED = 1;
 
-    /** An unknown error occurred while processing the call in the AppFunctionService. */
+    /**
+     * An unknown error occurred while processing the call in the AppFunctionService.
+     *
+     * <p>This error is thrown when the service is connected in the remote application but an
+     * unexpected error is thrown from the bound application.
+     */
     public static final int RESULT_APP_UNKNOWN_ERROR = 2;
 
-    /**
-     * An internal error occurred within AppFunctionManagerService.
-     *
-     * <p>This error may be considered similar to {@link IllegalStateException}
-     */
+    /** An internal unexpected error coming from the system. */
     public static final int RESULT_INTERNAL_ERROR = 3;
 
     /**
diff --git a/core/java/android/app/jank/StateTracker.java b/core/java/android/app/jank/StateTracker.java
new file mode 100644
index 0000000..cb457ff
--- /dev/null
+++ b/core/java/android/app/jank/StateTracker.java
@@ -0,0 +1,206 @@
+/*
+ * 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.app.jank;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.util.Pools.SimplePool;
+import android.view.Choreographer;
+
+import androidx.annotation.VisibleForTesting;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * StateTracker is responsible for keeping track of currently active states as well as
+ * previously encountered states. States are added, updated or removed by widgets that support state
+ * tracking. When a state is first added it will get a vsyncid associated to it, when that state
+ * is removed or updated to a different state it will have a second vsyncid associated with it. The
+ * two vsyncids create a range of ids where that particular state was active.
+ * @hide
+ */
+@FlaggedApi(Flags.FLAG_DETAILED_APP_JANK_METRICS_API)
+@VisibleForTesting
+public class StateTracker {
+
+    // Used to synchronize access to mPreviousStates.
+    private final Object mLock = new Object();
+    private Choreographer mChoreographer;
+
+    // The max number of StateData objects that will be stored in the pool for reuse.
+    private static final int MAX_POOL_SIZE = 500;
+    // The max number of currently active states to track.
+    protected static final int MAX_CONCURRENT_STATE_COUNT = 25;
+    // The maximum number of previously seen states that will be counted.
+    protected static final int MAX_PREVIOUSLY_ACTIVE_STATE_COUNT = 1000;
+
+    // Pool to store the previously used StateData objects to save recreating them each time.
+    private final SimplePool<StateData> mStateDataObjectPool = new SimplePool<>(MAX_POOL_SIZE);
+    // Previously encountered states that have not been associated to a frame.
+    private ArrayList<StateData> mPreviousStates = new ArrayList<>();
+    // Currently active widgets and widget states
+    private ConcurrentHashMap<String, StateData> mActiveStates = new ConcurrentHashMap<>();
+
+    public StateTracker(@NonNull Choreographer choreographer) {
+        mChoreographer = choreographer;
+    }
+
+    /**
+     * Updates the currentState to the nextState.
+     * @param widgetCategory preselected general widget category.
+     * @param widgetId developer defined widget id if available.
+     * @param currentState current state of the widget.
+     * @param nextState the state the widget will be in.
+     */
+    public void updateState(@NonNull String widgetCategory, @NonNull String widgetId,
+            @NonNull String currentState, @NonNull String nextState) {
+        // remove the now inactive state from the active states list
+        removeState(widgetCategory, widgetId, currentState);
+
+        // add the updated state to the active states list
+        putState(widgetCategory, widgetId, nextState);
+    }
+
+    /**
+     * Removes the state from the active state list and adds it to the previously encountered state
+     * list. Associates an end vsync id to the state.
+     * @param widgetCategory preselected general widget category.
+     * @param widgetId developer defined widget id if available.
+     * @param widgetState no longer active widget state.
+     */
+    public void removeState(@NonNull String widgetCategory, @NonNull String widgetId,
+            @NonNull String widgetState) {
+
+        String stateKey = getStateKey(widgetCategory, widgetId, widgetState);
+        // Check if we have the active state
+        StateData stateData = mActiveStates.remove(stateKey);
+
+        // If there are no states that match just return.
+        // This can happen if mActiveStates is at MAX_CONCURRENT_STATE_COUNT and a widget tries to
+        // remove a state that was never added or if a widget tries to remove the same state twice.
+        if (stateData == null) return;
+
+        synchronized (mLock) {
+            stateData.mVsyncIdEnd = mChoreographer.getVsyncId();
+            // Add the StateData to the previous state list.  We  need to keep a list of all the
+            // previously active states until we can process the next batch of frame data.
+            if (mPreviousStates.size() < MAX_PREVIOUSLY_ACTIVE_STATE_COUNT) {
+                mPreviousStates.add(stateData);
+            }
+        }
+    }
+
+    /**
+     * Adds a new state to the active state list. Associates a start vsync id to the state.
+     * @param widgetCategory preselected general widget category.
+     * @param widgetId developer defined widget id if available.
+     * @param widgetState the current active widget state.
+     */
+    public void putState(@NonNull String widgetCategory, @NonNull String widgetId,
+            @NonNull String widgetState) {
+
+        // Check if we can accept a new state
+        if (mActiveStates.size() >= MAX_CONCURRENT_STATE_COUNT) return;
+
+        String stateKey = getStateKey(widgetCategory, widgetId, widgetState);
+
+        // Check if there is currently any active states
+        // if there is already a state that matches then its presumed as still active.
+        if (mActiveStates.containsKey(stateKey)) return;
+
+        // Check if we have am unused state object in the pool
+        StateData stateData = mStateDataObjectPool.acquire();
+        if (stateData == null) {
+            stateData = new StateData();
+        }
+        stateData.mVsyncIdStart = mChoreographer.getVsyncId();
+        stateData.mStateDataKey = stateKey;
+        stateData.mWidgetState = widgetState;
+        stateData.mWidgetCategory = widgetCategory;
+        stateData.mWidgetId = widgetId;
+        stateData.mVsyncIdEnd = Long.MAX_VALUE;
+        mActiveStates.put(stateKey, stateData);
+
+    }
+
+    /**
+     * Will add all previously encountered states as well as all currently active states to the list
+     * that was passed in.
+     * @param allStates the list that will be populated with the widget states.
+     */
+    public void retrieveAllStates(ArrayList<StateData> allStates) {
+        synchronized (mLock) {
+            allStates.addAll(mPreviousStates);
+            allStates.addAll(mActiveStates.values());
+        }
+    }
+
+    /**
+     * Call after processing a batch of JankData, will remove any processed states from the
+     * previous state list.
+     */
+    public void stateProcessingComplete() {
+        synchronized (mLock) {
+            for (int i = mPreviousStates.size() - 1; i >= 0; i--) {
+                StateData stateData = mPreviousStates.get(i);
+                if (stateData.mProcessed) {
+                    mPreviousStates.remove(stateData);
+                    mStateDataObjectPool.release(stateData);
+                }
+            }
+        }
+    }
+
+    /**
+     * Only intended to be used for testing, this enables test methods to submit pending states
+     * with known start and end vsyncids.  This allows testing methods to know the exact ranges
+     * of vysncid and calculate exactly how many states should or should not be processed.
+     * @param stateData the data that will be added.
+     *
+     */
+    @VisibleForTesting
+    public void addPendingStateData(List<StateData> stateData) {
+        synchronized (mLock) {
+            mPreviousStates.addAll(stateData);
+        }
+    }
+
+    private String getStateKey(String widgetCategory, String widgetId, String widgetState) {
+        return widgetCategory + widgetId + widgetState;
+    }
+
+    /**
+     * @hide
+     */
+    @VisibleForTesting
+    public static class StateData {
+
+        // Concatenated string of widget category, widget state and widget id.
+        public String mStateDataKey;
+        public String mWidgetCategory;
+        public String mWidgetState;
+        public String mWidgetId;
+        // vsyncid when the state was first added.
+        public long mVsyncIdStart;
+        // vsyncid for when the state was removed.
+        public long mVsyncIdEnd;
+        // Used to indicate whether this state has been processed and can be returned to the pool.
+        public boolean mProcessed;
+    }
+}
diff --git a/core/java/android/app/metrics.aconfig b/core/java/android/app/metrics.aconfig
index 55d9c2d..488f1c7 100644
--- a/core/java/android/app/metrics.aconfig
+++ b/core/java/android/app/metrics.aconfig
@@ -8,12 +8,3 @@
      description: "Controls whether to report memory metrics post GC cleanup"
      bug: "331243037"
 }
-
-flag {
-     namespace: "system_performance"
-     name: "report_postgc_memory_metrics_readonly"
-     is_exported: false
-     description: "Controls whether to report memory metrics post GC cleanup (readonly)"
-     bug: "331243037"
-     is_fixed_read_only: true
-}
diff --git a/core/java/android/app/notification.aconfig b/core/java/android/app/notification.aconfig
index 1d4c18f..0fc4291 100644
--- a/core/java/android/app/notification.aconfig
+++ b/core/java/android/app/notification.aconfig
@@ -66,6 +66,16 @@
 }
 
 flag {
+  name: "modes_multiuser"
+  namespace: "systemui"
+  description: "Fixes for modes (and DND/Zen in general) when callers are not the current user"
+  bug: "323163267"
+  metadata {
+    purpose: PURPOSE_BUGFIX
+  }
+}
+
+flag {
   name: "api_tvextender"
   is_exported: true
   namespace: "systemui"
diff --git a/core/java/android/app/performance.aconfig b/core/java/android/app/performance.aconfig
new file mode 100644
index 0000000..7484a85
--- /dev/null
+++ b/core/java/android/app/performance.aconfig
@@ -0,0 +1,20 @@
+package: "android.app"
+container: "system"
+
+flag {
+     namespace: "system_performance"
+     name: "pic_uses_shared_memory"
+     is_exported: true
+     is_fixed_read_only: true
+     description: "PropertyInvalidatedCache uses shared memory for nonces."
+     bug: "366552454"
+}
+
+flag {
+     namespace: "system_performance"
+     name: "enforce_pic_testmode_protocol"
+     is_exported: true
+     is_fixed_read_only: true
+     description: "Enforce PropertyInvalidatedCache.setTestMode() protocol"
+     bug: "366552454"
+}
diff --git a/core/java/android/app/wallpaper.aconfig b/core/java/android/app/wallpaper.aconfig
index c5bd56f..4b880d0 100644
--- a/core/java/android/app/wallpaper.aconfig
+++ b/core/java/android/app/wallpaper.aconfig
@@ -14,3 +14,11 @@
   description: "Fixes timing of wallpaper changed notification and adds extra information. Only effective after rebooting."
   bug: "369814294"
 }
+
+flag {
+  name: "live_wallpaper_content_handling"
+  namespace: "systemui"
+  description: "Support for user-generated content in live wallpapers. Only effective after rebooting."
+  bug: "347235611"
+  is_exported: true
+}
diff --git a/core/java/android/app/wallpaper/WallpaperDescription.aidl b/core/java/android/app/wallpaper/WallpaperDescription.aidl
new file mode 100644
index 0000000..8c959b8
--- /dev/null
+++ b/core/java/android/app/wallpaper/WallpaperDescription.aidl
@@ -0,0 +1,20 @@
+
+/*
+** Copyright 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.app.wallpaper;
+
+parcelable WallpaperDescription;
diff --git a/core/java/android/app/wallpaper/WallpaperDescription.java b/core/java/android/app/wallpaper/WallpaperDescription.java
new file mode 100644
index 0000000..dedcb48
--- /dev/null
+++ b/core/java/android/app/wallpaper/WallpaperDescription.java
@@ -0,0 +1,426 @@
+/*
+ * 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.app.wallpaper;
+
+import static android.app.Flags.FLAG_LIVE_WALLPAPER_CONTENT_HANDLING;
+
+import android.annotation.FlaggedApi;
+import android.app.WallpaperInfo;
+import android.content.ComponentName;
+import android.net.Uri;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.PersistableBundle;
+import android.text.Html;
+import android.text.Spanned;
+import android.text.SpannedString;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.modules.utils.TypedXmlPullParser;
+import com.android.modules.utils.TypedXmlSerializer;
+
+import org.xmlpull.v1.XmlPullParser;
+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;
+
+/**
+ * Describes a wallpaper, including associated metadata and optional content to be used by its
+ * {@link android.service.wallpaper.WallpaperService.Engine}, the {@link ComponentName} to be used
+ * by {@link android.app.WallpaperManager}, and an optional id to differentiate between different
+ * distinct wallpapers rendered by the same wallpaper service.
+ *
+ * <p>This class is used to communicate among a wallpaper rendering service, a wallpaper chooser UI,
+ * and {@link android.app.WallpaperManager}. This class describes a specific instance of a live
+ * wallpaper, unlike {@link WallpaperInfo} which is common to all instances of a wallpaper
+ * component. Each {@link WallpaperDescription} can have distinct metadata.
+ * </p>
+ */
+@FlaggedApi(FLAG_LIVE_WALLPAPER_CONTENT_HANDLING)
+public final class WallpaperDescription implements Parcelable {
+    private static final String TAG = "WallpaperDescription";
+    private static final  String XML_TAG_CONTENT = "content";
+    private static final String XML_TAG_DESCRIPTION = "description";
+
+    @Nullable private final ComponentName mComponent;
+    @Nullable private final String mId;
+    @Nullable private final Uri mThumbnail;
+    @Nullable private final CharSequence mTitle;
+    @NonNull private final List<CharSequence> mDescription;
+    @Nullable private final Uri mContextUri;
+    @Nullable private final CharSequence mContextDescription;
+    @NonNull private final PersistableBundle mContent;
+
+    private WallpaperDescription(@Nullable ComponentName component,
+            @Nullable String id, @Nullable Uri thumbnail, @Nullable CharSequence title,
+            @Nullable List<CharSequence> description, @Nullable Uri contextUri,
+            @Nullable CharSequence contextDescription,
+            @Nullable PersistableBundle content) {
+        this.mComponent = component;
+        this.mId = id;
+        this.mThumbnail = thumbnail;
+        this.mTitle = title;
+        this.mDescription = (description != null) ? description : new ArrayList<>();
+        this.mContextUri = contextUri;
+        this.mContextDescription = contextDescription;
+        this.mContent = (content != null) ? content : new PersistableBundle();
+    }
+
+    /** @return the component for this wallpaper, or {@code null} for a static wallpaper */
+    @Nullable public ComponentName getComponent() {
+        return mComponent;
+    }
+
+    /** @return the id for this wallpaper, or {@code null} if not provided */
+    @Nullable public String getId() {
+        return mId;
+    }
+
+    /** @return the thumbnail for this wallpaper, or {@code null} if not provided */
+    @Nullable public Uri getThumbnail() {
+        return mThumbnail;
+    }
+
+    /**
+     * @return the title for this wallpaper, with each list element intended to be a separate
+     * line, or {@code null} if not provided
+     */
+    @Nullable public CharSequence getTitle() {
+        return mTitle;
+    }
+
+    /** @return the description for this wallpaper */
+    @NonNull
+    public List<CharSequence> getDescription() {
+        return new ArrayList<>();
+    }
+
+    /** @return the {@link Uri} for the action associated with the wallpaper, or {@code null} if not
+     * provided */
+    @Nullable public Uri getContextUri() {
+        return mContextUri;
+    }
+
+    /** @return the description for the action associated with the wallpaper, or {@code null} if not
+     * provided */
+    @Nullable public CharSequence getContextDescription() {
+        return mContextDescription;
+    }
+
+    /** @return any additional content required to render this wallpaper */
+    @NonNull
+    public PersistableBundle getContent() {
+        return mContent;
+    }
+
+    ////// Comparison overrides
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof WallpaperDescription that)) return false;
+        return Objects.equals(mComponent, that.mComponent) && Objects.equals(mId,
+                that.mId);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mComponent, mId);
+    }
+
+    ////// XML storage
+
+    /** @hide */
+    public void saveToXml(TypedXmlSerializer out) throws IOException, XmlPullParserException {
+        if (mComponent != null) {
+            out.attribute(null, "component", mComponent.flattenToShortString());
+        }
+        if (mId != null) out.attribute(null, "id", mId);
+        if (mThumbnail != null) out.attribute(null, "thumbnail", mThumbnail.toString());
+        if (mTitle != null) out.attribute(null, "title", toHtml(mTitle));
+        if (mContextUri != null) out.attribute(null, "contexturi", mContextUri.toString());
+        if (mContextDescription != null) {
+            out.attribute(null, "contextdescription", toHtml(mContextDescription));
+        }
+        out.startTag(null, XML_TAG_DESCRIPTION);
+        for (CharSequence s : mDescription) out.attribute(null, "descriptionline", toHtml(s));
+        out.endTag(null, XML_TAG_DESCRIPTION);
+        try {
+            out.startTag(null, XML_TAG_CONTENT);
+            mContent.saveToXml(out);
+        } catch (XmlPullParserException e) {
+            // Be extra conservative and don't fail when writing content since it could come
+            // from third parties
+            Log.e(TAG, "unable to convert wallpaper content to XML");
+        } finally {
+            out.endTag(null, XML_TAG_CONTENT);
+        }
+    }
+
+    /** @hide */
+    public static WallpaperDescription restoreFromXml(TypedXmlPullParser in) throws IOException,
+            XmlPullParserException {
+        final int outerDepth = in.getDepth();
+        String component = in.getAttributeValue(null, "component");
+        ComponentName componentName = (component != null) ? ComponentName.unflattenFromString(
+                component) : null;
+        String id = in.getAttributeValue(null, "id");
+        String thumbnailString = in.getAttributeValue(null, "thumbnail");
+        Uri thumbnail = (thumbnailString != null) ? Uri.parse(thumbnailString) : null;
+        CharSequence title = fromHtml(in.getAttributeValue(null, "title"));
+        String contextUriString = in.getAttributeValue(null, "contexturi");
+        Uri contextUri = (contextUriString != null) ? Uri.parse(contextUriString) : null;
+        CharSequence contextDescription = fromHtml(
+                in.getAttributeValue(null, "contextdescription"));
+
+        List<CharSequence> description = new ArrayList<>();
+        PersistableBundle content = null;
+        int type;
+        while ((type = in.next()) != XmlPullParser.END_DOCUMENT
+                && (type != XmlPullParser.END_TAG || in.getDepth() > outerDepth)) {
+            if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+                continue;
+            }
+            String name = in.getName();
+            if (XML_TAG_DESCRIPTION.equals(name)) {
+                for (int i = 0; i < in.getAttributeCount(); i++) {
+                    description.add(fromHtml(in.getAttributeValue(i)));
+                }
+            } else if (XML_TAG_CONTENT.equals(name)) {
+                content = PersistableBundle.restoreFromXml(in);
+            }
+        }
+
+        return new WallpaperDescription(componentName, id, thumbnail, title, description,
+                contextUri, contextDescription, content);
+    }
+
+    private static String toHtml(@NonNull CharSequence c) {
+        Spanned s = (c instanceof Spanned) ? (Spanned) c : new SpannedString(c);
+        return Html.toHtml(s, Html.TO_HTML_PARAGRAPH_LINES_INDIVIDUAL);
+    }
+
+    private static CharSequence fromHtml(@Nullable String text) {
+        if (text == null) {
+            return  null;
+        } else {
+            return removeTrailingWhitespace(Html.fromHtml(text, Html.FROM_HTML_MODE_COMPACT));
+        }
+    }
+
+    // Html.fromHtml and toHtml add a trailing line. This removes it. See
+    // https://stackoverflow.com/q/9589381
+    private static CharSequence removeTrailingWhitespace(CharSequence s) {
+        if (s == null) return null;
+
+        int end = s.length();
+        while (end > 0 && Character.isWhitespace(s.charAt(end - 1))) {
+            end--;
+        }
+
+        return s.subSequence(0, end);
+    }
+
+    ////// Parcelable implementation
+
+    WallpaperDescription(@NonNull Parcel in) {
+        mComponent = ComponentName.readFromParcel(in);
+        mId = in.readString8();
+        mThumbnail = Uri.CREATOR.createFromParcel(in);
+        mTitle = in.readCharSequence();
+        mDescription = Arrays.stream(in.readCharSequenceArray()).toList();
+        mContextUri = Uri.CREATOR.createFromParcel(in);
+        mContextDescription = in.readCharSequence();
+        mContent = PersistableBundle.CREATOR.createFromParcel(in);
+    }
+
+    @Nullable
+    public static final Creator<WallpaperDescription> CREATOR = new Creator<>() {
+        @Override
+        public WallpaperDescription createFromParcel(Parcel source) {
+            return new WallpaperDescription(source);
+        }
+
+        @Override
+        public WallpaperDescription[] newArray(int size) {
+            return new WallpaperDescription[size];
+        }
+    };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        ComponentName.writeToParcel(mComponent, dest);
+        dest.writeString8(mId);
+        Uri.writeToParcel(dest, mThumbnail);
+        dest.writeCharSequence(mTitle);
+        dest.writeCharSequenceArray(mDescription.toArray(new CharSequence[0]));
+        Uri.writeToParcel(dest, mContextUri);
+        dest.writeCharSequence(mContextDescription);
+        dest.writePersistableBundle(mContent);
+    }
+
+    ////// Builder
+
+    /**
+     * Convert the current description to a {@link Builder}.
+     * @return the Builder representing this description
+     */
+    @NonNull
+    public Builder toBuilder() {
+        return new Builder().setComponent(mComponent).setId(mId).setThumbnail(mThumbnail).setTitle(
+                mTitle).setDescription(mDescription).setContextUri(
+                mContextUri).setContextDescription(mContextDescription).setContent(mContent);
+    }
+
+    /** Builder for the immutable {@link WallpaperDescription} class */
+    public static final class Builder {
+        @Nullable private ComponentName mComponent;
+        @Nullable private String mId;
+        @Nullable private Uri mThumbnail;
+        @Nullable private CharSequence mTitle;
+        @NonNull private List<CharSequence> mDescription = new ArrayList<>();
+        @Nullable private Uri mContextUri;
+        @Nullable private CharSequence mContextDescription;
+        @NonNull private PersistableBundle mContent = new PersistableBundle();
+
+        /** Creates a new, empty {@link Builder}. */
+        public Builder() {}
+
+        /**
+         * Specify the component for this wallpaper.
+         *
+         * <p>This method is hidden because only trusted apps should be able to specify the
+         * component, which names a wallpaper service to be started by the system.
+         * </p>
+         *
+         * @param component component name, or {@code null} for static wallpaper
+         * @hide
+         */
+        @NonNull
+        public Builder setComponent(@Nullable ComponentName component) {
+            mComponent = component;
+            return this;
+        }
+
+        /**
+         * Set the id for this wallpaper.
+         *
+         * <p>IDs are used to distinguish among different instances of wallpapers rendered by the
+         * same component, and should be unique among all wallpapers for that component.
+         * </p>
+         *
+         * @param id the id, or {@code null} for none
+         */
+        @NonNull
+        public Builder setId(@Nullable String id) {
+            mId = id;
+            return this;
+        }
+
+        /**
+         * Set the thumbnail Uri for this wallpaper.
+         *
+         * @param thumbnail the thumbnail Uri, or {@code null} for none
+         */
+        @NonNull
+        public Builder setThumbnail(@Nullable Uri thumbnail) {
+            mThumbnail = thumbnail;
+            return this;
+        }
+
+        /**
+         * Set the title for this wallpaper.
+         *
+         * @param title the title, or {@code null} for none
+         */
+        @NonNull
+        public Builder setTitle(@Nullable CharSequence title) {
+            mTitle = title;
+            return this;
+        }
+
+        /**
+         * Set the description for this wallpaper. Each array element should be shown on a
+         * different line.
+         *
+         * @param description the description, or an empty list for none
+         */
+        @NonNull
+        public Builder setDescription(@NonNull List<CharSequence> description) {
+            mDescription = description;
+            return this;
+        }
+
+        /**
+         * Set the Uri for the action associated with this wallpaper, to be shown as a link with the
+         * wallpaper information.
+         *
+         * @param contextUri the action Uri, or {@code null} for no action
+         */
+        @NonNull
+        public Builder setContextUri(@Nullable Uri contextUri) {
+            mContextUri = contextUri;
+            return this;
+        }
+
+        /**
+         * Set the link text for the action associated with this wallpaper.
+         *
+         * @param contextDescription the link text, or {@code null} for default text
+         */
+        @NonNull
+        public Builder setContextDescription(@Nullable CharSequence contextDescription) {
+            mContextDescription = contextDescription;
+            return this;
+        }
+
+        /**
+         * Set the additional content required to render this wallpaper.
+         *
+         * <p>When setting additional content (asset id, etc.), best practice is to set an ID as
+         * well. This allows WallpaperManager and other code to distinguish between different
+         * wallpapers handled by this component.
+         * </p>
+         *
+         * @param content additional content, or an empty bundle for none
+         */
+        @NonNull
+        public Builder setContent(@NonNull PersistableBundle content) {
+            mContent = content;
+            return this;
+        }
+
+        /** Creates and returns the {@link WallpaperDescription} represented by this builder. */
+        @NonNull
+        public WallpaperDescription build() {
+            return new WallpaperDescription(mComponent, mId, mThumbnail, mTitle, mDescription,
+                    mContextUri, mContextDescription, mContent);
+        }
+    }
+}
diff --git a/core/java/android/app/wallpaper/WallpaperInstance.aidl b/core/java/android/app/wallpaper/WallpaperInstance.aidl
new file mode 100644
index 0000000..15a15be
--- /dev/null
+++ b/core/java/android/app/wallpaper/WallpaperInstance.aidl
@@ -0,0 +1,20 @@
+
+/*
+** Copyright 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.app.wallpaper;
+
+parcelable WallpaperInstance;
diff --git a/core/java/android/app/wallpaper/WallpaperInstance.java b/core/java/android/app/wallpaper/WallpaperInstance.java
new file mode 100644
index 0000000..48a649b
--- /dev/null
+++ b/core/java/android/app/wallpaper/WallpaperInstance.java
@@ -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 android.app.wallpaper;
+
+import static android.app.Flags.FLAG_LIVE_WALLPAPER_CONTENT_HANDLING;
+
+import android.annotation.FlaggedApi;
+import android.app.WallpaperInfo;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import java.util.Objects;
+
+/**
+ * Describes a wallpaper that has been set as a current wallpaper.
+ *
+ * <p>This class is used by {@link android.app.WallpaperManager} to store information about a
+ * wallpaper that is currently in use. Because it has been set as an active wallpaper it offers
+ * some guarantees that {@link WallpaperDescription} does not:
+ * <ul>
+ *     <li>It contains the {@link WallpaperInfo} corresponding to the
+ *     {@link android.content.ComponentName}</li> specified in the description
+ *     <li>{@link #getId()} is guaranteed to be non-null</li>
+ * </ul>
+ * </p>
+ */
+@FlaggedApi(FLAG_LIVE_WALLPAPER_CONTENT_HANDLING)
+public final class WallpaperInstance implements Parcelable {
+    private static final String DEFAULT_ID = "default_id";
+    @Nullable private final WallpaperInfo mInfo;
+    @NonNull private final WallpaperDescription mDescription;
+    @Nullable private final String mIdOverride;
+
+    /**
+     * Create a WallpaperInstance for the wallpaper given by {@link WallpaperDescription}.
+     *
+     * @param info the live wallpaper info for this wallpaper, or null if static
+     * @param description description of the wallpaper for this instance
+     */
+    public WallpaperInstance(@Nullable WallpaperInfo info,
+            @NonNull WallpaperDescription description) {
+        this(info, description, null);
+    }
+
+    /**
+     * Create a WallpaperInstance for the wallpaper given by {@link WallpaperDescription}.
+     *
+     * This is provided as an escape hatch to provide an explicit id for cases where the
+     * description id and {@link WallpaperInfo} are both {@code null}.
+     *
+     * @param info the live wallpaper info for this wallpaper, or null if static
+     * @param description description of the wallpaper for this instance
+     * @param idOverride optional id to override the value given in the description
+     *
+     * @hide
+     */
+    public WallpaperInstance(@Nullable WallpaperInfo info,
+            @NonNull WallpaperDescription description, @Nullable String idOverride) {
+        mInfo = info;
+        mDescription = description;
+        mIdOverride = idOverride;
+    }
+
+    /** @return the live wallpaper info, or {@code null} if static */
+    @Nullable public WallpaperInfo getInfo() {
+        return mInfo;
+    }
+
+    /**
+     * See {@link WallpaperDescription.Builder#getId()} for rules about id uniqueness.
+     *
+     * @return the ID of the wallpaper instance if given by the wallpaper description, otherwise a
+     * default value
+     */
+    @NonNull public String getId() {
+        if (mIdOverride != null) {
+            return mIdOverride;
+        } else if (mDescription.getId() != null) {
+            return mDescription.getId();
+        } else if (mInfo != null) {
+            return mInfo.getComponent().flattenToString();
+        } else {
+            return DEFAULT_ID;
+        }
+    }
+
+    /** @return the description for this wallpaper */
+    @NonNull public WallpaperDescription getDescription() {
+        return mDescription;
+    }
+
+    ////// Comparison overrides
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof WallpaperInstance that)) return false;
+        if (mInfo == null) {
+            return that.mInfo == null && Objects.equals(getId(), that.getId());
+        } else {
+            return that.mInfo != null
+                    && Objects.equals(mInfo.getComponent(), that.mInfo.getComponent())
+                    && Objects.equals(getId(), that.getId());
+        }
+    }
+
+    @Override
+    public int hashCode() {
+        return (mInfo != null) ? Objects.hash(mInfo.getComponent(), getId()) : Objects.hash(
+                getId());
+    }
+
+    ////// Parcelable implementation
+
+    WallpaperInstance(@NonNull Parcel in) {
+        mInfo = in.readTypedObject(WallpaperInfo.CREATOR);
+        mDescription = WallpaperDescription.CREATOR.createFromParcel(in);
+        mIdOverride = in.readString8();
+    }
+
+    @NonNull
+    public static final Creator<WallpaperInstance> CREATOR = new Creator<>() {
+        @Override
+        public WallpaperInstance createFromParcel(Parcel in) {
+            return new WallpaperInstance(in);
+        }
+
+        @Override
+        public WallpaperInstance[] newArray(int size) {
+            return new WallpaperInstance[size];
+        }
+    };
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeTypedObject(mInfo, flags);
+        mDescription.writeToParcel(dest, flags);
+        dest.writeString8(mIdOverride);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+}
diff --git a/core/java/android/app/wearable/IWearableSensingManager.aidl b/core/java/android/app/wearable/IWearableSensingManager.aidl
index c0d06ea..39a727f 100644
--- a/core/java/android/app/wearable/IWearableSensingManager.aidl
+++ b/core/java/android/app/wearable/IWearableSensingManager.aidl
@@ -31,8 +31,18 @@
  */
 interface IWearableSensingManager {
      @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE)")
+     int getAvailableConnectionCount();
+     @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE)")
      void provideConnection(in ParcelFileDescriptor parcelFileDescriptor, in IWearableSensingCallback wearableSensingCallback, in RemoteCallback statusCallback);
      @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE)")
+     int provideConcurrentConnection(in ParcelFileDescriptor parcelFileDescriptor, in PersistableBundle metadata, in IWearableSensingCallback wearableSensingCallback, in RemoteCallback statusCallback);
+     @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE)")
+     boolean removeConnection(int connectionId);
+     @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE)")
+     void removeAllConnections();
+     @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE)")
+     void provideReadOnlyParcelFileDescriptor(in ParcelFileDescriptor parcelFileDescriptor, in PersistableBundle metadata, in RemoteCallback statusCallback);
+     @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE)")
      void provideDataStream(in ParcelFileDescriptor parcelFileDescriptor, in @nullable IWearableSensingCallback wearableSensingCallback, in RemoteCallback statusCallback);
      @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE)")
      void provideData(in PersistableBundle data, in SharedMemory sharedMemory, in RemoteCallback callback);
diff --git a/core/java/android/app/wearable/WearableConnection.java b/core/java/android/app/wearable/WearableConnection.java
new file mode 100644
index 0000000..f4935b2
--- /dev/null
+++ b/core/java/android/app/wearable/WearableConnection.java
@@ -0,0 +1,64 @@
+/*
+ * 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.app.wearable;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.ParcelFileDescriptor;
+import android.os.PersistableBundle;
+
+import java.util.concurrent.Executor;
+import java.util.function.Consumer;
+
+/**
+ * A connection to a remote wearable device.
+ *
+ * @hide
+ */
+@SystemApi
+@FlaggedApi(Flags.FLAG_ENABLE_CONCURRENT_WEARABLE_CONNECTIONS)
+public interface WearableConnection {
+
+    /** Returns the connection to provide. */
+    @NonNull
+    ParcelFileDescriptor getConnection();
+
+    /** Returns the metadata related to this connection. */
+    @NonNull
+    PersistableBundle getMetadata();
+
+    /**
+     * Callback method called when the connection is accepted by the WearableSensingService.
+     *
+     * <p>See {@link WearableSensingManager#provideConnection(ParcelFileDescriptor, Executor,
+     * Consumer)} for details about the relationship between the connection provided via {@link
+     * #getConnection()} and the connection accepted by the WearableSensingService.
+     *
+     * <p>There will be no new invocation of this callback method after the connection is removed.
+     * Ongoing invocation will continue to run.
+     */
+    void onConnectionAccepted();
+
+    /**
+     * Callback method called when an error occurred during secure connection setup.
+     *
+     * <p>There will be no new invocation of this callback method after the connection is removed.
+     * Ongoing invocation will continue to run.
+     */
+    void onError(@WearableSensingManager.StatusCode int errorCode);
+}
diff --git a/core/java/android/app/wearable/WearableSensingDataRequest.java b/core/java/android/app/wearable/WearableSensingDataRequest.java
index 9329b37..adbc4ec 100644
--- a/core/java/android/app/wearable/WearableSensingDataRequest.java
+++ b/core/java/android/app/wearable/WearableSensingDataRequest.java
@@ -16,7 +16,6 @@
 
 package android.app.wearable;
 
-import android.annotation.FlaggedApi;
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
 import android.os.Parcel;
@@ -30,7 +29,6 @@
  *
  * @hide
  */
-@FlaggedApi(Flags.FLAG_ENABLE_DATA_REQUEST_OBSERVER_API)
 @SystemApi
 public final class WearableSensingDataRequest implements Parcelable {
     private static final int MAX_REQUEST_SIZE = 200;
@@ -164,7 +162,6 @@
     }
 
     /** A builder for WearableSensingDataRequest. */
-    @FlaggedApi(Flags.FLAG_ENABLE_DATA_REQUEST_OBSERVER_API)
     public static final class Builder {
         private int mDataType;
         private PersistableBundle mRequestDetails;
diff --git a/core/java/android/app/wearable/WearableSensingManager.java b/core/java/android/app/wearable/WearableSensingManager.java
index d28969d..f076375 100644
--- a/core/java/android/app/wearable/WearableSensingManager.java
+++ b/core/java/android/app/wearable/WearableSensingManager.java
@@ -54,20 +54,21 @@
 import java.io.OutputStream;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.Executor;
 import java.util.function.Consumer;
 
 /**
- * Allows granted apps to manage the WearableSensingService.
- * Applications are responsible for managing the connection to Wearables. Applications can choose
- * to provide a data stream to the WearableSensingService to use for
- * computing {@link AmbientContextEvent}s. Applications can also optionally provide their own
- * defined data to power the detection of {@link AmbientContextEvent}s.
- * Methods on this class requires the caller to hold and be granted the
- * {@link Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE}.
+ * Allows granted apps to manage the WearableSensingService. Applications are responsible for
+ * managing the connection to Wearables. Applications can choose to provide a data stream to the
+ * WearableSensingService to use for computing {@link AmbientContextEvent}s. Applications can also
+ * optionally provide their own defined data to power the detection of {@link AmbientContextEvent}s.
+ * Methods on this class requires the caller to hold and be granted the {@link
+ * Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE}.
  *
  * <p>The use of "Wearable" here is not the same as the Android Wear platform and should be treated
- * separately. </p>
+ * separately.
  *
  * @hide
  */
@@ -75,8 +76,8 @@
 @SystemService(Context.WEARABLE_SENSING_SERVICE)
 public class WearableSensingManager {
     /**
-     * The bundle key for the service status query result, used in
-     * {@code RemoteCallback#sendResult}.
+     * The bundle key for the service status query result, used in {@code
+     * RemoteCallback#sendResult}.
      *
      * @hide
      */
@@ -93,13 +94,20 @@
             "android.app.wearable.extra.WEARABLE_SENSING_DATA_REQUEST";
 
     /**
-     * An unknown status.
+     * An invalid connection ID returned by the system_server when it encounters an error providing
+     * a connection to WearableSensingService.
+     *
+     * @hide
      */
+    public static final int CONNECTION_ID_INVALID = -1;
+
+    /** A placeholder connection ID used in an implementation detail. */
+    private static final int CONNECTION_ID_PLACEHOLDER = -2;
+
+    /** An unknown status. */
     public static final int STATUS_UNKNOWN = 0;
 
-    /**
-     * The value of the status code that indicates success.
-     */
+    /** The value of the status code that indicates success. */
     public static final int STATUS_SUCCESS = 1;
 
     /**
@@ -107,46 +115,45 @@
      * supported.
      *
      * @deprecated WearableSensingManager does not deal with events. Use {@link
-     * STATUS_UNSUPPORTED_OPERATION} instead for operations not supported by the implementation of
-     * {@link WearableSensingService}.
+     *     STATUS_UNSUPPORTED_OPERATION} instead for operations not supported by the implementation
+     *     of {@link WearableSensingService}.
      */
-    @Deprecated
-    public static final int STATUS_UNSUPPORTED = 2;
+    @Deprecated public static final int STATUS_UNSUPPORTED = 2;
 
-    /**
-     * The value of the status code that indicates service not available.
-     */
+    /** The value of the status code that indicates service not available. */
     public static final int STATUS_SERVICE_UNAVAILABLE = 3;
 
-    /**
-     * The value of the status code that there's no connection to the wearable.
-     */
+    /** The value of the status code that there's no connection to the wearable. */
     public static final int STATUS_WEARABLE_UNAVAILABLE = 4;
 
-    /**
-     * The value of the status code that the app is not granted access.
-     */
+    /** The value of the status code that the app is not granted access. */
     public static final int STATUS_ACCESS_DENIED = 5;
 
     /**
      * The value of the status code that indicates the method called is not supported by the
      * implementation of {@link WearableSensingService}.
      */
-    @FlaggedApi(Flags.FLAG_ENABLE_UNSUPPORTED_OPERATION_STATUS_CODE)
     public static final int STATUS_UNSUPPORTED_OPERATION = 6;
 
     /**
      * The value of the status code that indicates an error occurred in the encrypted channel backed
-     * by the provided connection. See {@link #provideConnection(ParcelFileDescriptor,
-     * Executor, Consumer)}.
+     * by the provided connection. See {@link #provideConnection(ParcelFileDescriptor, Executor,
+     * Consumer)}.
      */
-    @FlaggedApi(Flags.FLAG_ENABLE_PROVIDE_WEARABLE_CONNECTION_API)
     public static final int STATUS_CHANNEL_ERROR = 7;
 
     /** The value of the status code that indicates the provided data type is not supported. */
-    @FlaggedApi(Flags.FLAG_ENABLE_DATA_REQUEST_OBSERVER_API)
     public static final int STATUS_UNSUPPORTED_DATA_TYPE = 8;
 
+    /**
+     * The value of the status code that indicates the provided connection is rejected because the
+     * maximum number of concurrent connections have already been provided. Use {@link
+     * #removeConnection(WearableConnection)} or {@link #removeAllConnections()} to remove a
+     * connection before providing a new one.
+     */
+    @FlaggedApi(Flags.FLAG_ENABLE_CONCURRENT_WEARABLE_CONNECTIONS)
+    public static final int STATUS_MAX_CONCURRENT_CONNECTIONS_EXCEEDED = 9;
+
     /** @hide */
     @IntDef(
             prefix = {"STATUS_"},
@@ -159,7 +166,8 @@
                 STATUS_ACCESS_DENIED,
                 STATUS_UNSUPPORTED_OPERATION,
                 STATUS_CHANNEL_ERROR,
-                STATUS_UNSUPPORTED_DATA_TYPE
+                STATUS_UNSUPPORTED_DATA_TYPE,
+                STATUS_MAX_CONCURRENT_CONNECTIONS_EXCEEDED
             })
     @Retention(RetentionPolicy.SOURCE)
     public @interface StatusCode {}
@@ -183,7 +191,6 @@
      * @return The WearableSensingDataRequest in the provided Intent, or null if the Intent does not
      *     contain a WearableSensingDataRequest.
      */
-    @FlaggedApi(Flags.FLAG_ENABLE_DATA_REQUEST_OBSERVER_API)
     @Nullable
     public static WearableSensingDataRequest getDataRequestFromIntent(@NonNull Intent intent) {
         return intent.getParcelableExtra(
@@ -194,8 +201,13 @@
     private final Context mContext;
     private final IWearableSensingManager mService;
 
+    private final Map<WearableConnection, Integer> mWearableConnectionIdMap =
+            new ConcurrentHashMap<>();
+
     /**
-     * {@hide}
+     * Creates a WearableSensingManager.
+     *
+     * @hide
      */
     public WearableSensingManager(Context context, IWearableSensingManager service) {
         mContext = context;
@@ -203,9 +215,61 @@
     }
 
     /**
+     * Returns the remaining number of concurrent connections allowed by {@link
+     * #provideConnection(WearableConnection, Executor)}.
+     */
+    @RequiresPermission(Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE)
+    @FlaggedApi(Flags.FLAG_ENABLE_CONCURRENT_WEARABLE_CONNECTIONS)
+    public int getAvailableConnectionCount() {
+        try {
+            return mService.getAvailableConnectionCount();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Provides a remote wearable device connection to the WearableSensingService and sends the
      * resulting status to the {@code statusConsumer} after the call.
      *
+     * <p>This method has the same behavior as {@link #provideConnection(WearableConnection,
+     * Executor)} except that concurrent connections are not allowed. Before providing the
+     * secureWearableConnection, the system will restart the WearableSensingService process if it
+     * has not been restarted since the last secureWearableConnection was provided via this method.
+     * Other method calls into WearableSensingService may be dropped during the restart. The caller
+     * is responsible for ensuring other method calls are queued until a success status is returned
+     * from the {@code statusConsumer}.
+     *
+     * <p>If an error occurred in the encrypted channel (such as the underlying stream closed), the
+     * system will send a status code of {@link STATUS_CHANNEL_ERROR} to the {@code statusConsumer}
+     * and kill the WearableSensingService process.
+     *
+     * @param wearableConnection The connection to provide
+     * @param executor Executor on which to run the consumer callback
+     * @param statusConsumer A consumer that handles the status codes for providing the connection
+     *     and errors in the encrypted channel.
+     */
+    @RequiresPermission(Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE)
+    public void provideConnection(
+            @NonNull ParcelFileDescriptor wearableConnection,
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull @StatusCode Consumer<Integer> statusConsumer) {
+        RemoteCallback statusCallback = createStatusCallback(executor, statusConsumer);
+        try {
+            // The wearableSensingCallback is included in this method call even though it is not
+            // semantically related to the connection because we want to avoid race conditions
+            // during the process restart triggered by this method call. See
+            // com.android.server.wearable.RemoteWearableSensingService for details.
+            mService.provideConnection(
+                    wearableConnection, createWearableSensingCallback(executor), statusCallback);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Provides a remote wearable device connection to the WearableSensingService.
+     *
      * <p>This is used by applications that will also provide an implementation of the isolated
      * WearableSensingService.
      *
@@ -228,15 +292,18 @@
      * from secureWearableConnection. The raw {@code wearableConnection} provided to this method
      * will not be directly available to the WearableSensingService.
      *
-     * <p>If an error occurred in the encrypted channel (such as the underlying stream closed), the
-     * system will send a status code of {@link STATUS_CHANNEL_ERROR} to the {@code statusConsumer}
-     * and kill the WearableSensingService process.
+     * <p>There is a limit on the number of concurrent connections allowed. Call {@link
+     * #getAvailableConnectionCount()} to check the remaining quota. If more connections are
+     * provided than allowed, the new connection will be rejected with {@value
+     * #STATUS_MAX_CONCURRENT_CONNECTIONS_EXCEEDED}. To reclaim the quota of a previously provided
+     * connection that is no longer needed, call {@link #removeConnection(WearableConnection)} with
+     * the same WearableConnection instance. Connections provided via {@link
+     * #provideConnection(ParcelFileDescriptor, Executor, Consumer)} will not contribute towards the
+     * concurrent connection limit.
      *
-     * <p>Before providing the secureWearableConnection, the system will restart the
-     * WearableSensingService process if it has not been restarted since the last
-     * secureWearableConnection was provided. Other method calls into WearableSensingService may be
-     * dropped during the restart. The caller is responsible for ensuring other method calls are
-     * queued until a success status is returned from the {@code statusConsumer}.
+     * <p>If the {@code wearableConnection} receives an error, either the connection will not be
+     * sent to the WearableSensingService, or the connection will be closed by the system. Either
+     * way, the concurrent connection quota for this connection will be automatically released.
      *
      * <p>If the WearableSensingService implementation belongs to the same APK as the caller,
      * calling this method will allow WearableSensingService to read from the caller's file
@@ -244,24 +311,144 @@
      * caller's process and executed by the {@code executor} provided to this method.
      *
      * @param wearableConnection The connection to provide
-     * @param executor Executor on which to run the consumer callback
-     * @param statusConsumer A consumer that handles the status codes for providing the connection
-     *     and errors in the encrypted channel.
+     * @param executor Executor on which to run the callback methods on {@code wearableConnection}
      */
     @RequiresPermission(Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE)
-    @FlaggedApi(Flags.FLAG_ENABLE_PROVIDE_WEARABLE_CONNECTION_API)
+    @FlaggedApi(Flags.FLAG_ENABLE_CONCURRENT_WEARABLE_CONNECTIONS)
     public void provideConnection(
-            @NonNull ParcelFileDescriptor wearableConnection,
+            @NonNull WearableConnection wearableConnection, @NonNull Executor executor) {
+        RemoteCallback statusCallback =
+                createStatusCallback(
+                        executor,
+                        statusCode -> {
+                            if (!mWearableConnectionIdMap.containsKey(wearableConnection)) {
+                                Slog.i(
+                                        TAG,
+                                        "Surpassed status callback for removed connection "
+                                                + wearableConnection);
+                                return;
+                            }
+                            if (statusCode == STATUS_SUCCESS) {
+                                wearableConnection.onConnectionAccepted();
+                            } else {
+                                mWearableConnectionIdMap.remove(wearableConnection);
+                                wearableConnection.onError(statusCode);
+                            }
+                        });
+        try {
+            // The statusCallback should not invoke callback on a removed connection. To implement
+            // this behavior, statusCallback will only invoke the callback if the connection is
+            // present in mWearableConnectionIdMap. We need to add the connection to the map before
+            // statusCallback is sent to mService in case the system triggers the statusCallback
+            // before the connectionId is returned.
+            mWearableConnectionIdMap.put(wearableConnection, CONNECTION_ID_PLACEHOLDER);
+            int connectionId =
+                    mService.provideConcurrentConnection(
+                            wearableConnection.getConnection(),
+                            wearableConnection.getMetadata(),
+                            createWearableSensingCallback(executor),
+                            statusCallback);
+            if (connectionId != CONNECTION_ID_INVALID) {
+                mWearableConnectionIdMap.put(wearableConnection, connectionId);
+            }
+            // For invalid connection IDs, the status callback will remove the connection from
+            // mWearableConnectionIdMap
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Removes a connection previously provided via {@link #provideConnection(WearableConnection,
+     * Executor)}.
+     *
+     * <p>The WearableSensingService will no longer be able to use this connection.
+     *
+     * <p>After this method returns, there will be no new invocation to callback methods in the
+     * removed {@link WearableConnection}. Ongoing invocations will continue to run.
+     *
+     * <p>This method does nothing if the provided {@code wearableConnection} does not match any
+     * open connection.
+     *
+     * <p>This method should not be called before the corresponding {@link
+     * #provideConnection(WearableConnection, Executor)} invocation returns. Otherwise, the
+     * connection may not be removed.
+     *
+     * @param wearableConnection The WearableConnection instance previously provided to {@link
+     *     #provideConnection(WearableConnection, Executor)}.
+     * @return true if a concurrent connection quota has been freed due to this method invocation.
+     *     Returns false otherwise.
+     */
+    @RequiresPermission(Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE)
+    @FlaggedApi(Flags.FLAG_ENABLE_CONCURRENT_WEARABLE_CONNECTIONS)
+    public boolean removeConnection(@NonNull WearableConnection wearableConnection) {
+        int connectionId =
+                mWearableConnectionIdMap.getOrDefault(wearableConnection, CONNECTION_ID_INVALID);
+        if (connectionId == CONNECTION_ID_INVALID) {
+            return false;
+        }
+        if (connectionId == CONNECTION_ID_PLACEHOLDER) {
+            Slog.w(
+                    TAG,
+                    "Attempt to remove connection before provideConnection returns. The connection"
+                            + " will not be removed.");
+            return false;
+        }
+        mWearableConnectionIdMap.remove(wearableConnection);
+        try {
+            return mService.removeConnection(connectionId);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Removes all connections previously provided via {@link #provideConnection(WearableConnection,
+     * Executor)}.
+     *
+     * <p>The connection provided via {@link #provideConnection(ParcelFileDescriptor, Executor,
+     * Consumer)}, if exists, will not be affected by this method.
+     *
+     * <p>The WearableSensingService will no longer be able to use any of the removed connections.
+     *
+     * <p>After this method returns, there will be no new invocation to callback methods in the
+     * removed {@link WearableConnection}s. Ongoing invocations will continue to run.
+     */
+    @RequiresPermission(Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE)
+    @FlaggedApi(Flags.FLAG_ENABLE_CONCURRENT_WEARABLE_CONNECTIONS)
+    public void removeAllConnections() {
+        mWearableConnectionIdMap.clear();
+        try {
+            mService.removeAllConnections();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Provides a read-only {@link ParcelFileDescriptor} to the WearableSensingService.
+     *
+     * <p>This is used by the application that will also provide an implementation of the isolated
+     * WearableSensingService. If the {@link ParcelFileDescriptor} was provided successfully, {@link
+     * WearableSensingManager#STATUS_SUCCESS} will be sent to the {@code statusConsumer}.
+     *
+     * @param parcelFileDescriptor The read-only {@link ParcelFileDescriptor} to provide
+     * @param metadata Metadata used to identify the {@code parcelFileDescriptor}
+     * @param executor Executor on which to run the {@code statusConsumer}
+     * @param statusConsumer A consumer that handles the status codes
+     * @throws IllegalArgumentException when the {@code parcelFileDescriptor} is not read-only
+     */
+    @RequiresPermission(Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE)
+    @FlaggedApi(Flags.FLAG_ENABLE_PROVIDE_READ_ONLY_PFD)
+    public void provideReadOnlyParcelFileDescriptor(
+            @NonNull ParcelFileDescriptor parcelFileDescriptor,
+            @NonNull PersistableBundle metadata,
             @NonNull @CallbackExecutor Executor executor,
             @NonNull @StatusCode Consumer<Integer> statusConsumer) {
         RemoteCallback statusCallback = createStatusCallback(executor, statusConsumer);
         try {
-            // The wearableSensingCallback is included in this method call even though it is not
-            // semantically related to the connection because we want to avoid race conditions
-            // during the process restart triggered by this method call. See
-            // com.android.server.wearable.RemoteWearableSensingService for details.
-            mService.provideConnection(
-                    wearableConnection, createWearableSensingCallback(executor), statusCallback);
+            mService.provideReadOnlyParcelFileDescriptor(
+                    parcelFileDescriptor, metadata, statusCallback);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -284,8 +471,8 @@
      * @param executor Executor on which to run the consumer callback
      * @param statusConsumer A consumer that handles the status codes, which is returned right after
      *     the call.
-     * @deprecated Use {@link #provideConnection(ParcelFileDescriptor, Executor, Consumer)} instead
-     *     to provide a remote wearable device connection to the WearableSensingService
+     * @deprecated Use {@link #provideConnection(WearableConnection, Executor)} instead to provide a
+     *     remote wearable device connection to the WearableSensingService
      */
     @Deprecated
     @RequiresPermission(Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE)
@@ -308,28 +495,27 @@
 
     /**
      * Sets configuration and provides read-only data in a {@link PersistableBundle} that may be
-     * used by the WearableSensingService, and sends the result to the {@link Consumer}
-     * right after the call. It is dependent on the application to
-     * define the type of data to provide. This is used by applications that will also
-     * provide an implementation of an isolated WearableSensingService. If the data was
-     * provided successfully {@link WearableSensingManager#STATUS_SUCCESS} will be povided.
+     * used by the WearableSensingService, and sends the result to the {@link Consumer} right after
+     * the call. It is dependent on the application to define the type of data to provide. This is
+     * used by applications that will also provide an implementation of an isolated
+     * WearableSensingService. If the data was provided successfully {@link
+     * WearableSensingManager#STATUS_SUCCESS} will be povided.
      *
      * @param data Application configuration data to provide to the {@link WearableSensingService}.
-     *             PersistableBundle does not allow any remotable objects or other contents
-     *             that can be used to communicate with other processes.
-     * @param sharedMemory The unrestricted data blob to
-     *                     provide to the {@link WearableSensingService}. Use this to provide the
-     *                     sensing models data or other such data to the trusted process.
-     *                     The sharedMemory must be read only and protected with
-     *                     {@link OsConstants.PROT_READ}.
-     *                     Other operations will be removed by the system.
+     *     PersistableBundle does not allow any remotable objects or other contents that can be used
+     *     to communicate with other processes.
+     * @param sharedMemory The unrestricted data blob to provide to the {@link
+     *     WearableSensingService}. Use this to provide the sensing models data or other such data
+     *     to the trusted process. The sharedMemory must be read only and protected with {@link
+     *     OsConstants.PROT_READ}. Other operations will be removed by the system.
      * @param executor Executor on which to run the consumer callback
-     * @param statusConsumer A consumer that handles the status codes, which is returned
-     *                     right after the call
+     * @param statusConsumer A consumer that handles the status codes, which is returned right after
+     *     the call
      */
     @RequiresPermission(Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE)
     public void provideData(
-            @NonNull PersistableBundle data, @Nullable SharedMemory sharedMemory,
+            @NonNull PersistableBundle data,
+            @Nullable SharedMemory sharedMemory,
             @NonNull @CallbackExecutor Executor executor,
             @NonNull @StatusCode Consumer<Integer> statusConsumer) {
         try {
@@ -390,7 +576,6 @@
      * @param executor Executor on which to run the consumer callback.
      * @param statusConsumer A consumer that handles the status code for the observer registration.
      */
-    @FlaggedApi(Flags.FLAG_ENABLE_DATA_REQUEST_OBSERVER_API)
     @RequiresPermission(Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE)
     public void registerDataRequestObserver(
             int dataType,
@@ -417,7 +602,6 @@
      * @param statusConsumer A consumer that handles the status code for the observer
      *     unregistration.
      */
-    @FlaggedApi(Flags.FLAG_ENABLE_DATA_REQUEST_OBSERVER_API)
     @RequiresPermission(Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE)
     public void unregisterDataRequestObserver(
             int dataType,
@@ -449,8 +633,8 @@
      * Consumer<android.service.voice.HotwordAudioStream>}, the system will check whether the {@link
      * android.service.voice.VoiceInteractionService} at that time is {@code
      * targetVisComponentName}. If not, the system will call {@link
-     * WearableSensingService#onStopHotwordAudioStream()} and will not forward the audio
-     * data to the current {@link android.service.voice.HotwordDetectionService} nor {@link
+     * WearableSensingService#onStopHotwordAudioStream()} and will not forward the audio data to the
+     * current {@link android.service.voice.HotwordDetectionService} nor {@link
      * android.service.voice.VoiceInteractionService}. The system will not send a status code to
      * {@code statusConsumer} regarding the {@code targetVisComponentName} check. The caller is
      * responsible for determining whether the system's {@link
@@ -466,9 +650,9 @@
      * <p>If the {@code statusConsumer} returns {@link STATUS_SUCCESS}, the caller should call
      * {@link #stopHotwordRecognition(Executor, Consumer)} when it wants the wearable to stop
      * listening for hotword. If the {@code statusConsumer} returns any other status code, a failure
-     * has occurred and calling {@link #stopHotwordRecognition(Executor, Consumer)} is not
-     * required. The system will not retry listening automatically. The caller should call this
-     * method again if they want to retry.
+     * has occurred and calling {@link #stopHotwordRecognition(Executor, Consumer)} is not required.
+     * The system will not retry listening automatically. The caller should call this method again
+     * if they want to retry.
      *
      * <p>If a failure occurred after the {@link statusConsumer} returns {@link STATUS_SUCCESS},
      * {@link statusConsumer} will be invoked again with a status code other than {@link
diff --git a/core/java/android/app/wearable/flags.aconfig b/core/java/android/app/wearable/flags.aconfig
index 534f461..acb3ff8 100644
--- a/core/java/android/app/wearable/flags.aconfig
+++ b/core/java/android/app/wearable/flags.aconfig
@@ -46,4 +46,12 @@
     namespace: "machine_learning"
     description: "This flag enables the APIs for providing multiple concurrent connections to the WearableSensingService."
     bug: "358133158"
-}
\ No newline at end of file
+}
+
+flag {
+    name: "enable_provide_read_only_pfd"
+    is_exported: true
+    namespace: "machine_learning"
+    description: "This flag enables the APIs for providing read-only ParcelFileDescriptors to the WearableSensingService."
+    bug: "358130861"
+}
diff --git a/core/java/android/appwidget/AppWidgetHostView.java b/core/java/android/appwidget/AppWidgetHostView.java
index 933c336..df1028e 100644
--- a/core/java/android/appwidget/AppWidgetHostView.java
+++ b/core/java/android/appwidget/AppWidgetHostView.java
@@ -716,8 +716,8 @@
                         mCurrentSize);
             } else {
                 applyContent(null, false, e);
+                mLastExecutionSignal = null;
             }
-            mLastExecutionSignal = null;
         }
     }
 
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 07106e8..5893da3 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -4325,6 +4325,7 @@
            //@hide: ECM_ENHANCED_CONFIRMATION_SERVICE,
             CONTACT_KEYS_SERVICE,
             RANGING_SERVICE,
+            MEDIA_QUALITY_SERVICE,
             ADVANCED_PROTECTION_SERVICE,
 
     })
@@ -6767,6 +6768,17 @@
     public static final String SUPERVISION_SERVICE = "supervision";
 
     /**
+     * Use with {@link #getSystemService(String)} to retrieve a
+     * {@link android.media.quality.MediaQuality} for standardize picture and audio
+     * API parameters.
+     *
+     * @see #getSystemService(String)
+     * @see android.media.quality.MediaQuality
+     */
+    @FlaggedApi(android.media.tv.flags.Flags.FLAG_MEDIA_QUALITY_FW)
+    public static final String MEDIA_QUALITY_SERVICE = "media_quality";
+
+    /**
      * Determine whether the given permission is allowed for a particular
      * process and user ID running in the system.
      *
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index e8cec70..9bec598 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -6184,7 +6184,6 @@
      * {@link #EXTRA_CHOOSER_RESULT_INTENT_SENDER}.
      * </p>
      */
-    @FlaggedApi(android.service.chooser.Flags.FLAG_CHOOSER_PAYLOAD_TOGGLING)
     public static final String EXTRA_CHOOSER_ADDITIONAL_CONTENT_URI =
             "android.intent.extra.CHOOSER_ADDITIONAL_CONTENT_URI";
 
@@ -6194,7 +6193,6 @@
      * An integer, zero-based index into {@link #EXTRA_STREAM} argument indicating the item that
      * should be focused by the Chooser in preview.
      */
-    @FlaggedApi(android.service.chooser.Flags.FLAG_CHOOSER_PAYLOAD_TOGGLING)
     public static final String EXTRA_CHOOSER_FOCUSED_ITEM_POSITION =
             "android.intent.extra.CHOOSER_FOCUSED_ITEM_POSITION";
 
@@ -11672,6 +11670,7 @@
                 Log.w(TAG, "Failure filling in extras", e);
             }
         }
+        mCreatorTokenInfo = other.mCreatorTokenInfo;
         if (mayHaveCopiedUris && mContentUserHint == UserHandle.USER_CURRENT
                 && other.mContentUserHint != UserHandle.USER_CURRENT) {
             mContentUserHint = other.mContentUserHint;
@@ -12225,6 +12224,13 @@
     }
 
     /** @hide */
+    public void removeCreatorToken() {
+        if (mCreatorTokenInfo != null) {
+            mCreatorTokenInfo.mCreatorToken = null;
+        }
+    }
+
+    /** @hide */
     public @Nullable IBinder getCreatorToken() {
         return mCreatorTokenInfo == null ? null : mCreatorTokenInfo.mCreatorToken;
     }
@@ -12251,7 +12257,7 @@
     public void collectExtraIntentKeys() {
         if (!isPreventIntentRedirectEnabled()) return;
 
-        if (mExtras != null && !mExtras.isParcelled() && !mExtras.isEmpty()) {
+        if (mExtras != null && !mExtras.isEmpty()) {
             for (String key : mExtras.keySet()) {
                 if (mExtras.get(key) instanceof Intent) {
                     if (mCreatorTokenInfo == null) {
@@ -12833,6 +12839,8 @@
     private boolean isImageCaptureIntent() {
         return (MediaStore.ACTION_IMAGE_CAPTURE.equals(mAction)
                 || MediaStore.ACTION_IMAGE_CAPTURE_SECURE.equals(mAction)
+                || MediaStore.ACTION_MOTION_PHOTO_CAPTURE.equals(mAction)
+                || MediaStore.ACTION_MOTION_PHOTO_CAPTURE_SECURE.equals(mAction)
                 || MediaStore.ACTION_VIDEO_CAPTURE.equals(mAction));
     }
 
diff --git a/core/java/android/content/pm/IPackageInstaller.aidl b/core/java/android/content/pm/IPackageInstaller.aidl
index c911326..4fdbf1e9e 100644
--- a/core/java/android/content/pm/IPackageInstaller.aidl
+++ b/core/java/android/content/pm/IPackageInstaller.aidl
@@ -94,9 +94,9 @@
     @JavaPassthrough(annotation="@android.annotation.RequiresPermission(anyOf={android.Manifest.permission.INSTALL_PACKAGES,android.Manifest.permission.REQUEST_INSTALL_PACKAGES})")
     void reportUnarchivalStatus(int unarchiveId, int status, long requiredStorageBytes, in PendingIntent userActionIntent, in UserHandle userHandle);
 
-    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT)")
-    int getVerificationPolicy();
+    @EnforcePermission("VERIFICATION_AGENT")
+    int getVerificationPolicy(int userId);
 
-    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT)")
-    boolean setVerificationPolicy(int policy);
+    @EnforcePermission("VERIFICATION_AGENT")
+    boolean setVerificationPolicy(int policy, int userId);
 }
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index 5da1444..54c5596 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -1613,7 +1613,7 @@
     @RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT)
     public final @VerificationPolicy int getVerificationPolicy() {
         try {
-            return mInstaller.getVerificationPolicy();
+            return mInstaller.getVerificationPolicy(mUserId);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -1631,7 +1631,7 @@
     @RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT)
     public final boolean setVerificationPolicy(@VerificationPolicy int policy) {
         try {
-            return mInstaller.setVerificationPolicy(policy);
+            return mInstaller.setVerificationPolicy(policy, mUserId);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 47d9f09..3152ff4 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -146,6 +146,7 @@
      * This exception is thrown when a given package, application, or component
      * name cannot be found.
      */
+    @android.ravenwood.annotation.RavenwoodKeepWholeClass
     public static class NameNotFoundException extends AndroidException {
         public NameNotFoundException() {
         }
diff --git a/core/java/android/content/pm/UserProperties.java b/core/java/android/content/pm/UserProperties.java
index 1d4403c..8e02ffd 100644
--- a/core/java/android/content/pm/UserProperties.java
+++ b/core/java/android/content/pm/UserProperties.java
@@ -16,9 +16,11 @@
 
 package android.content.pm;
 
+import android.Manifest;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
 import android.annotation.SuppressLint;
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
@@ -513,6 +515,7 @@
      * Note that, internally, this does not perform an exact copy.
      * @hide
      */
+    @SuppressLint("MissingPermission")
     public UserProperties(UserProperties orig,
             boolean exposeAllFields,
             boolean hasManagePermission,
@@ -614,12 +617,10 @@
      *    {@link #SHOW_IN_SETTINGS_SEPARATE},
      *    and {@link #SHOW_IN_SETTINGS_NO}.
      *
-     * <p> The caller must have {@link android.Manifest.permission#MANAGE_USERS} to query this
-     * property.
-     *
      * @return whether, and how, a profile should be shown in the Settings.
      * @hide
      */
+    @RequiresPermission(Manifest.permission.MANAGE_USERS)
     public @ShowInSettings int getShowInSettings() {
         if (isPresent(INDEX_SHOW_IN_SETTINGS)) return mShowInSettings;
         if (mDefaultProperties != null) return mDefaultProperties.mShowInSettings;
@@ -690,6 +691,8 @@
     /**
      * Returns whether a profile should be started when its parent starts (unless in quiet mode).
      * This only applies for users that have parents (i.e. for profiles).
+     *
+     * Only available to the SYSTEM uid.
      * @hide
      */
     public boolean getStartWithParent() {
@@ -708,6 +711,8 @@
      * Returns whether an app in the profile should be deleted when the same package in
      * the parent user is being deleted.
      * This only applies for users that have parents (i.e. for profiles).
+     *
+     * Only available to the SYSTEM uid.
      * @hide
      */
     public boolean getDeleteAppWithParent() {
@@ -726,6 +731,8 @@
      * Returns whether the user should always
      * be {@link android.os.UserManager#isUserVisible() visible}.
      * The intended usage is for the Communal Profile, which is running and accessible at all times.
+     *
+     * Only available to the SYSTEM uid.
      * @hide
      */
     public boolean getAlwaysVisible() {
@@ -747,6 +754,7 @@
      * Possible return values include
      * {@link #INHERIT_DEVICE_POLICY_FROM_PARENT} or {@link #INHERIT_DEVICE_POLICY_NO}
      *
+     * Only available to the SYSTEM uid.
      * @hide
      */
     public @InheritDevicePolicy int getInheritDevicePolicy() {
@@ -777,6 +785,7 @@
      * @return whether contacts access from an associated profile is enabled for the user
      * @hide
      */
+    @RequiresPermission(Manifest.permission.MANAGE_USERS)
     public boolean getUseParentsContacts() {
         if (isPresent(INDEX_USE_PARENTS_CONTACTS)) return mUseParentsContacts;
         if (mDefaultProperties != null) return mDefaultProperties.mUseParentsContacts;
@@ -796,7 +805,9 @@
 
     /**
      * Returns true if user needs to update default
-     * {@link com.android.server.pm.CrossProfileIntentFilter} with its parents during an OTA update
+     * {@link com.android.server.pm.CrossProfileIntentFilter} with its parents during an OTA update.
+     *
+     * Only available to the SYSTEM uid.
      * @hide
      */
     public boolean getUpdateCrossProfileIntentFiltersOnOTA() {
@@ -863,6 +874,7 @@
      * checks is not guaranteed when the property is false and may vary depending on user types.
      * @hide
      */
+    @RequiresPermission(Manifest.permission.MANAGE_USERS)
     public boolean isAuthAlwaysRequiredToDisableQuietMode() {
         if (isPresent(INDEX_AUTH_ALWAYS_REQUIRED_TO_DISABLE_QUIET_MODE)) {
             return mAuthAlwaysRequiredToDisableQuietMode;
@@ -894,6 +906,8 @@
      * locking for a user can happen if either the device configuration is set or if this property
      * is set. When both, the config and the property value is false, the user storage is always
      * locked when the user is stopped.
+     *
+     * Only available to the SYSTEM uid.
      * @hide
      */
     public boolean getAllowStoppingUserWithDelayedLocking() {
@@ -915,6 +929,8 @@
 
     /**
      * Returns the user's {@link CrossProfileIntentFilterAccessControlLevel}.
+     *
+     * Only available to the SYSTEM uid.
      * @hide
      */
     public @CrossProfileIntentFilterAccessControlLevel int
@@ -944,6 +960,7 @@
      * Returns the user's {@link CrossProfileIntentResolutionStrategy}.
      * @return user's {@link CrossProfileIntentResolutionStrategy}.
      *
+     * Only available to the SYSTEM uid.
      * @hide
      */
     public @CrossProfileIntentResolutionStrategy int getCrossProfileIntentResolutionStrategy() {
@@ -1052,32 +1069,47 @@
 
     @Override
     public String toString() {
+        StringBuilder s = new StringBuilder();
+        s.append("UserProperties{");
+        s.append("mPropertiesPresent="); s.append(Long.toBinaryString(mPropertiesPresent));
+        try {
+            s.append(listPropertiesAsStringBuilder());
+        } catch (SecurityException e) {
+            // Caller doesn't have permission to see all the properties. Just don't share them.
+        }
+        s.append("}");
+        return s.toString();
+    }
+
+    private StringBuilder listPropertiesAsStringBuilder() {
+        final StringBuilder s = new StringBuilder();
+
         // Please print in increasing order of PropertyIndex.
-        return "UserProperties{"
-                + "mPropertiesPresent=" + Long.toBinaryString(mPropertiesPresent)
-                + ", mShowInLauncher=" + getShowInLauncher()
-                + ", mStartWithParent=" + getStartWithParent()
-                + ", mShowInSettings=" + getShowInSettings()
-                + ", mInheritDevicePolicy=" + getInheritDevicePolicy()
-                + ", mUseParentsContacts=" + getUseParentsContacts()
-                + ", mUpdateCrossProfileIntentFiltersOnOTA="
-                + getUpdateCrossProfileIntentFiltersOnOTA()
-                + ", mCrossProfileIntentFilterAccessControl="
-                + getCrossProfileIntentFilterAccessControl()
-                + ", mCrossProfileIntentResolutionStrategy="
-                + getCrossProfileIntentResolutionStrategy()
-                + ", mMediaSharedWithParent=" + isMediaSharedWithParent()
-                + ", mCredentialShareableWithParent=" + isCredentialShareableWithParent()
-                + ", mAuthAlwaysRequiredToDisableQuietMode="
-                + isAuthAlwaysRequiredToDisableQuietMode()
-                + ", mAllowStoppingUserWithDelayedLocking="
-                + getAllowStoppingUserWithDelayedLocking()
-                + ", mDeleteAppWithParent=" + getDeleteAppWithParent()
-                + ", mAlwaysVisible=" + getAlwaysVisible()
-                + ", mCrossProfileContentSharingStrategy=" + getCrossProfileContentSharingStrategy()
-                + ", mProfileApiVisibility=" + getProfileApiVisibility()
-                + ", mItemsRestrictedOnHomeScreen=" + areItemsRestrictedOnHomeScreen()
-                + "}";
+        s.append(", mShowInLauncher="); s.append(getShowInLauncher());
+        s.append(", mStartWithParent="); s.append(getStartWithParent());
+        s.append(", mShowInSettings="); s.append(getShowInSettings());
+        s.append(", mInheritDevicePolicy="); s.append(getInheritDevicePolicy());
+        s.append(", mUseParentsContacts="); s.append(getUseParentsContacts());
+        s.append(", mUpdateCrossProfileIntentFiltersOnOTA=");
+        s.append(getUpdateCrossProfileIntentFiltersOnOTA());
+        s.append(", mCrossProfileIntentFilterAccessControl=");
+        s.append(getCrossProfileIntentFilterAccessControl());
+        s.append(", mCrossProfileIntentResolutionStrategy=");
+        s.append(getCrossProfileIntentResolutionStrategy());
+        s.append(", mMediaSharedWithParent="); s.append(isMediaSharedWithParent());
+        s.append(", mCredentialShareableWithParent="); s.append(isCredentialShareableWithParent());
+        s.append(", mAuthAlwaysRequiredToDisableQuietMode=");
+        s.append(isAuthAlwaysRequiredToDisableQuietMode());
+        s.append(", mAllowStoppingUserWithDelayedLocking=");
+        s.append(getAllowStoppingUserWithDelayedLocking());
+        s.append(", mDeleteAppWithParent="); s.append(getDeleteAppWithParent());
+        s.append(", mAlwaysVisible="); s.append(getAlwaysVisible());
+        s.append(", mCrossProfileContentSharingStrategy=");
+        s.append(getCrossProfileContentSharingStrategy());
+        s.append(", mProfileApiVisibility="); s.append(getProfileApiVisibility());
+        s.append(", mItemsRestrictedOnHomeScreen="); s.append(areItemsRestrictedOnHomeScreen());
+
+        return s;
     }
 
     /**
diff --git a/core/java/android/content/pm/flags.aconfig b/core/java/android/content/pm/flags.aconfig
index 5f439b1..6f70586 100644
--- a/core/java/android/content/pm/flags.aconfig
+++ b/core/java/android/content/pm/flags.aconfig
@@ -297,14 +297,6 @@
 }
 
 flag {
-    name: "get_packages_from_launcher_apps"
-    namespace: "package_manager_service"
-    description: "Feature flag to provide the new methods within launcher apps class to get packages."
-    bug: "363324203"
-    is_fixed_read_only: true
-}
-
-flag {
     name: "remove_cross_user_permission_hack"
     namespace: "package_manager_service"
     description: "Feature flag to remove hack code of using PackageManager.MATCH_ANY_USER flag without cross user permission."
@@ -328,6 +320,13 @@
 }
 
 flag {
+    name: "sdk_dependency_installer"
+    namespace: "package_manager_service"
+    description: "Feature flag to enable installation of missing sdk dependency of app"
+    bug: "370822870"
+}
+
+flag {
     name: "include_feature_flags_in_package_cacher"
     namespace: "package_manager_service"
     description: "Include feature flag status when determining hits or misses in PackageCacher."
diff --git a/core/java/android/content/pm/parsing/ApkLite.java b/core/java/android/content/pm/parsing/ApkLite.java
index 19a13db..c83cf96 100644
--- a/core/java/android/content/pm/parsing/ApkLite.java
+++ b/core/java/android/content/pm/parsing/ApkLite.java
@@ -28,6 +28,7 @@
 import com.android.internal.util.CollectionUtils;
 import com.android.internal.util.DataClass;
 
+import java.util.Collections;
 import java.util.List;
 import java.util.Set;
 
@@ -141,6 +142,27 @@
     private final boolean mIsSdkLibrary;
 
     /**
+     * List of SDK names used by this apk.
+     */
+    private final @NonNull List<String> mUsesSdkLibraries;
+
+    /**
+     * List of SDK major versions used by this apk.
+     */
+    private final @Nullable long[] mUsesSdkLibrariesVersionsMajor;
+
+    /**
+     * List of SDK certificates used by this apk.
+     */
+    private final @Nullable String[][] mUsesSdkLibrariesCertDigests;
+
+    private final @NonNull List<String> mUsesStaticLibraries;
+
+    private final @Nullable long[] mUsesStaticLibrariesVersions;
+
+    private final @Nullable String[][] mUsesStaticLibrariesCertDigests;
+
+    /**
      * Indicates if this system app can be updated.
      */
     private final boolean mUpdatableSystem;
@@ -167,7 +189,12 @@
             String requiredSystemPropertyName, String requiredSystemPropertyValue,
             int minSdkVersion, int targetSdkVersion, int rollbackDataPolicy,
             Set<String> requiredSplitTypes, Set<String> splitTypes,
-            boolean hasDeviceAdminReceiver, boolean isSdkLibrary, boolean updatableSystem,
+            boolean hasDeviceAdminReceiver, boolean isSdkLibrary,
+            List<String> usesSdkLibraries, long[] usesSdkLibrariesVersionsMajor,
+            String[][] usesSdkLibrariesCertDigests,
+            List<String> usesStaticLibraries, long[] usesStaticLibrariesVersionsMajor,
+            String[][] usesStaticLibrariesCertDigests,
+            boolean updatableSystem,
             String emergencyInstaller, List<SharedLibraryInfo> declaredLibraries) {
         mPath = path;
         mPackageName = packageName;
@@ -202,6 +229,12 @@
         mRollbackDataPolicy = rollbackDataPolicy;
         mHasDeviceAdminReceiver = hasDeviceAdminReceiver;
         mIsSdkLibrary = isSdkLibrary;
+        mUsesSdkLibraries = usesSdkLibraries;
+        mUsesSdkLibrariesVersionsMajor = usesSdkLibrariesVersionsMajor;
+        mUsesSdkLibrariesCertDigests = usesSdkLibrariesCertDigests;
+        mUsesStaticLibraries = usesStaticLibraries;
+        mUsesStaticLibrariesVersions = usesStaticLibrariesVersionsMajor;
+        mUsesStaticLibrariesCertDigests = usesStaticLibrariesCertDigests;
         mUpdatableSystem = updatableSystem;
         mEmergencyInstaller = emergencyInstaller;
         mArchivedPackage = null;
@@ -242,6 +275,12 @@
         mRollbackDataPolicy = 0;
         mHasDeviceAdminReceiver = false;
         mIsSdkLibrary = false;
+        mUsesSdkLibraries = Collections.emptyList();
+        mUsesSdkLibrariesVersionsMajor = null;
+        mUsesSdkLibrariesCertDigests = null;
+        mUsesStaticLibraries = Collections.emptyList();
+        mUsesStaticLibrariesVersions = null;
+        mUsesStaticLibrariesCertDigests = null;
         mUpdatableSystem = true;
         mEmergencyInstaller = null;
         mArchivedPackage = archivedPackage;
@@ -555,6 +594,45 @@
     }
 
     /**
+     * List of SDK names used by this apk.
+     */
+    @DataClass.Generated.Member
+    public @NonNull List<String> getUsesSdkLibraries() {
+        return mUsesSdkLibraries;
+    }
+
+    /**
+     * List of SDK major versions used by this apk.
+     */
+    @DataClass.Generated.Member
+    public @Nullable long[] getUsesSdkLibrariesVersionsMajor() {
+        return mUsesSdkLibrariesVersionsMajor;
+    }
+
+    /**
+     * List of SDK certificates used by this apk.
+     */
+    @DataClass.Generated.Member
+    public @Nullable String[][] getUsesSdkLibrariesCertDigests() {
+        return mUsesSdkLibrariesCertDigests;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull List<String> getUsesStaticLibraries() {
+        return mUsesStaticLibraries;
+    }
+
+    @DataClass.Generated.Member
+    public @Nullable long[] getUsesStaticLibrariesVersions() {
+        return mUsesStaticLibrariesVersions;
+    }
+
+    @DataClass.Generated.Member
+    public @Nullable String[][] getUsesStaticLibrariesCertDigests() {
+        return mUsesStaticLibrariesCertDigests;
+    }
+
+    /**
      * Indicates if this system app can be updated.
      */
     @DataClass.Generated.Member
@@ -584,10 +662,10 @@
     }
 
     @DataClass.Generated(
-            time = 1728333566322L,
+            time = 1730202160705L,
             codegenVersion = "1.0.23",
             sourceFile = "frameworks/base/core/java/android/content/pm/parsing/ApkLite.java",
-            inputSignatures = "private final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.NonNull java.lang.String mPath\nprivate final @android.annotation.Nullable java.lang.String mSplitName\nprivate final @android.annotation.Nullable java.lang.String mUsesSplitName\nprivate final @android.annotation.Nullable java.lang.String mConfigForSplit\nprivate final @android.annotation.Nullable java.util.Set<java.lang.String> mRequiredSplitTypes\nprivate final @android.annotation.Nullable java.util.Set<java.lang.String> mSplitTypes\nprivate final  int mVersionCodeMajor\nprivate final  int mVersionCode\nprivate final  int mRevisionCode\nprivate final  int mInstallLocation\nprivate final  int mMinSdkVersion\nprivate final  int mTargetSdkVersion\nprivate final @android.annotation.NonNull android.content.pm.VerifierInfo[] mVerifiers\nprivate final @android.annotation.NonNull android.content.pm.SigningDetails mSigningDetails\nprivate final  boolean mFeatureSplit\nprivate final  boolean mIsolatedSplits\nprivate final  boolean mSplitRequired\nprivate final  boolean mCoreApp\nprivate final  boolean mDebuggable\nprivate final  boolean mProfileableByShell\nprivate final  boolean mMultiArch\nprivate final  boolean mUse32bitAbi\nprivate final  boolean mExtractNativeLibs\nprivate final  boolean mUseEmbeddedDex\nprivate final @android.annotation.Nullable java.lang.String mTargetPackageName\nprivate final  boolean mOverlayIsStatic\nprivate final  int mOverlayPriority\nprivate final @android.annotation.Nullable java.lang.String mRequiredSystemPropertyName\nprivate final @android.annotation.Nullable java.lang.String mRequiredSystemPropertyValue\nprivate final  int mRollbackDataPolicy\nprivate final  boolean mHasDeviceAdminReceiver\nprivate final  boolean mIsSdkLibrary\nprivate final  boolean mUpdatableSystem\nprivate final @android.annotation.Nullable java.lang.String mEmergencyInstaller\nprivate final @android.annotation.NonNull java.util.List<android.content.pm.SharedLibraryInfo> mDeclaredLibraries\nprivate final @android.annotation.Nullable android.content.pm.ArchivedPackageParcel mArchivedPackage\npublic  long getLongVersionCode()\nprivate  boolean hasAnyRequiredSplitTypes()\nclass ApkLite extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false, genConstDefs=false)")
+            inputSignatures = "private final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.NonNull java.lang.String mPath\nprivate final @android.annotation.Nullable java.lang.String mSplitName\nprivate final @android.annotation.Nullable java.lang.String mUsesSplitName\nprivate final @android.annotation.Nullable java.lang.String mConfigForSplit\nprivate final @android.annotation.Nullable java.util.Set<java.lang.String> mRequiredSplitTypes\nprivate final @android.annotation.Nullable java.util.Set<java.lang.String> mSplitTypes\nprivate final  int mVersionCodeMajor\nprivate final  int mVersionCode\nprivate final  int mRevisionCode\nprivate final  int mInstallLocation\nprivate final  int mMinSdkVersion\nprivate final  int mTargetSdkVersion\nprivate final @android.annotation.NonNull android.content.pm.VerifierInfo[] mVerifiers\nprivate final @android.annotation.NonNull android.content.pm.SigningDetails mSigningDetails\nprivate final  boolean mFeatureSplit\nprivate final  boolean mIsolatedSplits\nprivate final  boolean mSplitRequired\nprivate final  boolean mCoreApp\nprivate final  boolean mDebuggable\nprivate final  boolean mProfileableByShell\nprivate final  boolean mMultiArch\nprivate final  boolean mUse32bitAbi\nprivate final  boolean mExtractNativeLibs\nprivate final  boolean mUseEmbeddedDex\nprivate final @android.annotation.Nullable java.lang.String mTargetPackageName\nprivate final  boolean mOverlayIsStatic\nprivate final  int mOverlayPriority\nprivate final @android.annotation.Nullable java.lang.String mRequiredSystemPropertyName\nprivate final @android.annotation.Nullable java.lang.String mRequiredSystemPropertyValue\nprivate final  int mRollbackDataPolicy\nprivate final  boolean mHasDeviceAdminReceiver\nprivate final  boolean mIsSdkLibrary\nprivate final @android.annotation.NonNull java.util.List<java.lang.String> mUsesSdkLibraries\nprivate final @android.annotation.Nullable long[] mUsesSdkLibrariesVersionsMajor\nprivate final @android.annotation.Nullable java.lang.String[][] mUsesSdkLibrariesCertDigests\nprivate final @android.annotation.NonNull java.util.List<java.lang.String> mUsesStaticLibraries\nprivate final @android.annotation.Nullable long[] mUsesStaticLibrariesVersions\nprivate final @android.annotation.Nullable java.lang.String[][] mUsesStaticLibrariesCertDigests\nprivate final  boolean mUpdatableSystem\nprivate final @android.annotation.Nullable java.lang.String mEmergencyInstaller\nprivate final @android.annotation.NonNull java.util.List<android.content.pm.SharedLibraryInfo> mDeclaredLibraries\nprivate final @android.annotation.Nullable android.content.pm.ArchivedPackageParcel mArchivedPackage\npublic  long getLongVersionCode()\nprivate  boolean hasAnyRequiredSplitTypes()\nclass ApkLite extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false, genConstDefs=false)")
     @Deprecated
     private void __metadata() {}
 
diff --git a/core/java/android/content/pm/parsing/ApkLiteParseUtils.java b/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
index 1a7f628..34c3f57 100644
--- a/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
+++ b/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
@@ -32,6 +32,7 @@
 import android.content.res.ApkAssets;
 import android.content.res.XmlResourceParser;
 import android.os.Build;
+import android.os.SystemProperties;
 import android.os.Trace;
 import android.text.TextUtils;
 import android.util.ArrayMap;
@@ -44,6 +45,7 @@
 import com.android.internal.util.ArrayUtils;
 
 import libcore.io.IoUtils;
+import libcore.util.HexEncoding;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
@@ -88,6 +90,8 @@
     private static final String TAG_USES_SDK = "uses-sdk";
     private static final String TAG_USES_SPLIT = "uses-split";
     private static final String TAG_MANIFEST = "manifest";
+    private static final String TAG_USES_SDK_LIBRARY = "uses-sdk-library";
+    private static final String TAG_USES_STATIC_LIBRARY = "uses-static-library";
     private static final String TAG_SDK_LIBRARY = "sdk-library";
     private static final int SDK_VERSION = Build.VERSION.SDK_INT;
     private static final String[] SDK_CODENAMES = Build.VERSION.ACTIVE_CODENAMES;
@@ -460,6 +464,14 @@
         boolean hasDeviceAdminReceiver = false;
 
         boolean isSdkLibrary = false;
+        List<String> usesSdkLibraries = new ArrayList<>();
+        long[] usesSdkLibrariesVersionsMajor = new long[0];
+        String[][] usesSdkLibrariesCertDigests = new String[0][0];
+
+        List<String> usesStaticLibraries = new ArrayList<>();
+        long[] usesStaticLibrariesVersions = new long[0];
+        String[][] usesStaticLibrariesCertDigests = new String[0][0];
+
         List<SharedLibraryInfo> declaredLibraries = new ArrayList<>();
 
         // Only search the tree when the tag is the direct child of <manifest> tag
@@ -523,6 +535,102 @@
                             hasDeviceAdminReceiver |= isDeviceAdminReceiver(parser,
                                     hasBindDeviceAdminPermission);
                             break;
+                        case TAG_USES_SDK_LIBRARY:
+                            String usesSdkLibName = parser.getAttributeValue(
+                                    ANDROID_RES_NAMESPACE, "name");
+                            long usesSdkLibVersionMajor = parser.getAttributeIntValue(
+                                    ANDROID_RES_NAMESPACE, "versionMajor", -1);
+                            String usesSdkCertDigest = parser.getAttributeValue(
+                                     ANDROID_RES_NAMESPACE, "certDigest");
+
+                            if (usesSdkLibName == null || usesSdkLibName.isBlank()
+                                    || usesSdkLibVersionMajor < 0) {
+                                return input.error(
+                                        PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
+                                        "Bad uses-sdk-library declaration name: "
+                                                + usesSdkLibName
+                                                + " version: " + usesSdkLibVersionMajor);
+                            }
+
+                            if (usesSdkLibraries.contains(usesSdkLibName)) {
+                                return input.error(
+                                        PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
+                                        "Bad uses-sdk-library declaration. Depending on"
+                                                + " multiple versions of SDK library: "
+                                                + usesSdkLibName);
+                            }
+
+                            usesSdkLibraries.add(usesSdkLibName);
+                            usesSdkLibrariesVersionsMajor = ArrayUtils.appendLong(
+                                    usesSdkLibrariesVersionsMajor, usesSdkLibVersionMajor,
+                                    /*allowDuplicates=*/ true);
+
+                            // We allow ":" delimiters in the SHA declaration as this is the format
+                            // emitted by the certtool making it easy for developers to copy/paste.
+                            // TODO(372862145): Add test for this replacement
+                            usesSdkCertDigest = usesSdkCertDigest.replace(":", "").toLowerCase();
+
+                            if ("".equals(usesSdkCertDigest)) {
+                                // Test-only uses-sdk-library empty certificate digest override.
+                                usesSdkCertDigest = SystemProperties.get(
+                                        "debug.pm.uses_sdk_library_default_cert_digest", "");
+                                // Validate the overridden digest.
+                                try {
+                                    HexEncoding.decode(usesSdkCertDigest, false);
+                                } catch (IllegalArgumentException e) {
+                                    usesSdkCertDigest = "";
+                                }
+                            }
+                            // TODO(372862145): Add support for multiple signer
+                            usesSdkLibrariesCertDigests = ArrayUtils.appendElement(String[].class,
+                                    usesSdkLibrariesCertDigests, new String[]{usesSdkCertDigest},
+                                    /*allowDuplicates=*/ true);
+                            break;
+                        case TAG_USES_STATIC_LIBRARY:
+                            String usesStaticLibName = parser.getAttributeValue(
+                                    ANDROID_RES_NAMESPACE, "name");
+                            long usesStaticLibVersion = parser.getAttributeIntValue(
+                                    ANDROID_RES_NAMESPACE, "version", -1);
+                            String usesStaticLibCertDigest = parser.getAttributeValue(
+                                    ANDROID_RES_NAMESPACE, "certDigest");
+
+                            if (usesStaticLibName == null || usesStaticLibName.isBlank()
+                                    || usesStaticLibVersion < 0
+                                    || usesStaticLibCertDigest == null) {
+                                return input.error(
+                                        PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
+                                        "Bad uses-static-library declaration name: "
+                                                + usesStaticLibName
+                                                + " version: " + usesStaticLibVersion
+                                                + " certDigest: " + usesStaticLibCertDigest);
+                            }
+
+                            if (usesStaticLibraries.contains(usesStaticLibName)) {
+                                return input.error(
+                                        PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
+                                        "Bad uses-sdk-library declaration. Depending on"
+                                                + " multiple versions of static library: "
+                                                + usesStaticLibName);
+                            }
+
+                            usesStaticLibraries.add(usesStaticLibName);
+                            usesStaticLibrariesVersions = ArrayUtils.appendLong(
+                                    usesStaticLibrariesVersions, usesStaticLibVersion,
+                                    /*allowDuplicates=*/ true);
+
+                            // We allow ":" delimiters in the SHA declaration as this is the format
+                            // emitted by the certtool making it easy for developers to copy/paste.
+                            // TODO(372862145): Add test for this replacement
+                            usesStaticLibCertDigest =
+                                    usesStaticLibCertDigest.replace(":", "").toLowerCase();
+
+                            // TODO(372862145): Add support for multiple signer for app targeting
+                            //  O-MR1
+                            usesStaticLibrariesCertDigests = ArrayUtils.appendElement(
+                                    String[].class, usesStaticLibrariesCertDigests,
+                                    new String[]{usesStaticLibCertDigest},
+                                    /*allowDuplicates=*/ true);
+                            break;
                         case TAG_SDK_LIBRARY:
                             isSdkLibrary = true;
                             // Mirrors ParsingPackageUtils#parseSdkLibrary until lite and full
@@ -534,7 +642,7 @@
                             if (sdkLibName == null || sdkLibVersionMajor < 0) {
                                 return input.error(
                                         PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
-                                        "Bad uses-sdk-library declaration name: " + sdkLibName
+                                        "Bad sdk-library declaration name: " + sdkLibName
                                                 + " version: " + sdkLibVersionMajor);
                             }
                             declaredLibraries.add(new SharedLibraryInfo(
@@ -694,7 +802,10 @@
                         overlayIsStatic, overlayPriority, requiredSystemPropertyName,
                         requiredSystemPropertyValue, minSdkVersion, targetSdkVersion,
                         rollbackDataPolicy, requiredSplitTypes.first, requiredSplitTypes.second,
-                        hasDeviceAdminReceiver, isSdkLibrary, updatableSystem, emergencyInstaller,
+                        hasDeviceAdminReceiver, isSdkLibrary, usesSdkLibraries,
+                        usesSdkLibrariesVersionsMajor, usesSdkLibrariesCertDigests,
+                        usesStaticLibraries, usesStaticLibrariesVersions,
+                        usesStaticLibrariesCertDigests, updatableSystem, emergencyInstaller,
                         declaredLibraries));
     }
 
diff --git a/core/java/android/content/pm/parsing/PackageLite.java b/core/java/android/content/pm/parsing/PackageLite.java
index 9a2ee7f..76b25fe 100644
--- a/core/java/android/content/pm/parsing/PackageLite.java
+++ b/core/java/android/content/pm/parsing/PackageLite.java
@@ -115,6 +115,18 @@
      */
     private final boolean mIsSdkLibrary;
 
+    private final @NonNull List<String> mUsesSdkLibraries;
+
+    private final @Nullable long[] mUsesSdkLibrariesVersionsMajor;
+
+    private final @Nullable String[][] mUsesSdkLibrariesCertDigests;
+
+    private final @NonNull List<String> mUsesStaticLibraries;
+
+    private final @Nullable long[] mUsesStaticLibrariesVersions;
+
+    private final @Nullable String[][] mUsesStaticLibrariesCertDigests;
+
     private final @NonNull List<SharedLibraryInfo> mDeclaredLibraries;
 
     /**
@@ -149,6 +161,12 @@
         mSplitRequired = (baseApk.isSplitRequired() || hasAnyRequiredSplitTypes());
         mProfileableByShell = baseApk.isProfileableByShell();
         mIsSdkLibrary = baseApk.isIsSdkLibrary();
+        mUsesSdkLibraries = baseApk.getUsesSdkLibraries();
+        mUsesSdkLibrariesVersionsMajor = baseApk.getUsesSdkLibrariesVersionsMajor();
+        mUsesSdkLibrariesCertDigests = baseApk.getUsesSdkLibrariesCertDigests();
+        mUsesStaticLibraries = baseApk.getUsesStaticLibraries();
+        mUsesStaticLibrariesVersions = baseApk.getUsesStaticLibrariesVersions();
+        mUsesStaticLibrariesCertDigests = baseApk.getUsesStaticLibrariesCertDigests();
         mSplitNames = splitNames;
         mSplitTypes = splitTypes;
         mIsFeatureSplits = isFeatureSplits;
@@ -438,6 +456,36 @@
     }
 
     @DataClass.Generated.Member
+    public @NonNull List<String> getUsesSdkLibraries() {
+        return mUsesSdkLibraries;
+    }
+
+    @DataClass.Generated.Member
+    public @Nullable long[] getUsesSdkLibrariesVersionsMajor() {
+        return mUsesSdkLibrariesVersionsMajor;
+    }
+
+    @DataClass.Generated.Member
+    public @Nullable String[][] getUsesSdkLibrariesCertDigests() {
+        return mUsesSdkLibrariesCertDigests;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull List<String> getUsesStaticLibraries() {
+        return mUsesStaticLibraries;
+    }
+
+    @DataClass.Generated.Member
+    public @Nullable long[] getUsesStaticLibrariesVersions() {
+        return mUsesStaticLibrariesVersions;
+    }
+
+    @DataClass.Generated.Member
+    public @Nullable String[][] getUsesStaticLibrariesCertDigests() {
+        return mUsesStaticLibrariesCertDigests;
+    }
+
+    @DataClass.Generated.Member
     public @NonNull List<SharedLibraryInfo> getDeclaredLibraries() {
         return mDeclaredLibraries;
     }
@@ -451,10 +499,10 @@
     }
 
     @DataClass.Generated(
-            time = 1728333569917L,
+            time = 1730203707341L,
             codegenVersion = "1.0.23",
             sourceFile = "frameworks/base/core/java/android/content/pm/parsing/PackageLite.java",
-            inputSignatures = "private final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.NonNull java.lang.String mPath\nprivate final @android.annotation.NonNull java.lang.String mBaseApkPath\nprivate final @android.annotation.Nullable java.lang.String[] mSplitApkPaths\nprivate final @android.annotation.Nullable java.lang.String[] mSplitNames\nprivate final @android.annotation.Nullable java.lang.String[] mUsesSplitNames\nprivate final @android.annotation.Nullable java.lang.String[] mConfigForSplit\nprivate final @android.annotation.Nullable java.util.Set<java.lang.String> mBaseRequiredSplitTypes\nprivate final @android.annotation.Nullable java.util.Set<java.lang.String>[] mRequiredSplitTypes\nprivate final @android.annotation.Nullable java.util.Set<java.lang.String>[] mSplitTypes\nprivate final  int mVersionCodeMajor\nprivate final  int mVersionCode\nprivate final  int mTargetSdk\nprivate final  int mBaseRevisionCode\nprivate final @android.annotation.Nullable int[] mSplitRevisionCodes\nprivate final  int mInstallLocation\nprivate final @android.annotation.NonNull android.content.pm.VerifierInfo[] mVerifiers\nprivate final @android.annotation.NonNull android.content.pm.SigningDetails mSigningDetails\nprivate final @android.annotation.Nullable boolean[] mIsFeatureSplits\nprivate final  boolean mIsolatedSplits\nprivate final  boolean mSplitRequired\nprivate final  boolean mCoreApp\nprivate final  boolean mDebuggable\nprivate final  boolean mMultiArch\nprivate final  boolean mUse32bitAbi\nprivate final  boolean mExtractNativeLibs\nprivate final  boolean mProfileableByShell\nprivate final  boolean mUseEmbeddedDex\nprivate final  boolean mIsSdkLibrary\nprivate final @android.annotation.NonNull java.util.List<android.content.pm.SharedLibraryInfo> mDeclaredLibraries\nprivate final @android.annotation.Nullable android.content.pm.ArchivedPackageParcel mArchivedPackage\npublic  java.util.List<java.lang.String> getAllApkPaths()\npublic  long getLongVersionCode()\nprivate  boolean hasAnyRequiredSplitTypes()\nclass PackageLite extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false, genConstDefs=false)")
+            inputSignatures = "private final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.NonNull java.lang.String mPath\nprivate final @android.annotation.NonNull java.lang.String mBaseApkPath\nprivate final @android.annotation.Nullable java.lang.String[] mSplitApkPaths\nprivate final @android.annotation.Nullable java.lang.String[] mSplitNames\nprivate final @android.annotation.Nullable java.lang.String[] mUsesSplitNames\nprivate final @android.annotation.Nullable java.lang.String[] mConfigForSplit\nprivate final @android.annotation.Nullable java.util.Set<java.lang.String> mBaseRequiredSplitTypes\nprivate final @android.annotation.Nullable java.util.Set<java.lang.String>[] mRequiredSplitTypes\nprivate final @android.annotation.Nullable java.util.Set<java.lang.String>[] mSplitTypes\nprivate final  int mVersionCodeMajor\nprivate final  int mVersionCode\nprivate final  int mTargetSdk\nprivate final  int mBaseRevisionCode\nprivate final @android.annotation.Nullable int[] mSplitRevisionCodes\nprivate final  int mInstallLocation\nprivate final @android.annotation.NonNull android.content.pm.VerifierInfo[] mVerifiers\nprivate final @android.annotation.NonNull android.content.pm.SigningDetails mSigningDetails\nprivate final @android.annotation.Nullable boolean[] mIsFeatureSplits\nprivate final  boolean mIsolatedSplits\nprivate final  boolean mSplitRequired\nprivate final  boolean mCoreApp\nprivate final  boolean mDebuggable\nprivate final  boolean mMultiArch\nprivate final  boolean mUse32bitAbi\nprivate final  boolean mExtractNativeLibs\nprivate final  boolean mProfileableByShell\nprivate final  boolean mUseEmbeddedDex\nprivate final  boolean mIsSdkLibrary\nprivate final @android.annotation.NonNull java.util.List<java.lang.String> mUsesSdkLibraries\nprivate final @android.annotation.Nullable long[] mUsesSdkLibrariesVersionsMajor\nprivate final @android.annotation.Nullable java.lang.String[][] mUsesSdkLibrariesCertDigests\nprivate final @android.annotation.NonNull java.util.List<java.lang.String> mUsesStaticLibraries\nprivate final @android.annotation.Nullable long[] mUsesStaticLibrariesVersions\nprivate final @android.annotation.Nullable java.lang.String[][] mUsesStaticLibrariesCertDigests\nprivate final @android.annotation.NonNull java.util.List<android.content.pm.SharedLibraryInfo> mDeclaredLibraries\nprivate final @android.annotation.Nullable android.content.pm.ArchivedPackageParcel mArchivedPackage\npublic  java.util.List<java.lang.String> getAllApkPaths()\npublic  long getLongVersionCode()\nprivate  boolean hasAnyRequiredSplitTypes()\nclass PackageLite extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false, genConstDefs=false)")
     @Deprecated
     private void __metadata() {}
 
diff --git a/core/java/android/content/pm/verify/pkg/IVerificationSessionInterface.aidl b/core/java/android/content/pm/verify/pkg/IVerificationSessionInterface.aidl
index 66caf2d..2ab7452 100644
--- a/core/java/android/content/pm/verify/pkg/IVerificationSessionInterface.aidl
+++ b/core/java/android/content/pm/verify/pkg/IVerificationSessionInterface.aidl
@@ -24,16 +24,9 @@
  * @hide
  */
 interface IVerificationSessionInterface {
-    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT)")
     long getTimeoutTime(int verificationId);
-    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT)")
     long extendTimeRemaining(int verificationId, long additionalMs);
-    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT)")
     boolean setVerificationPolicy(int verificationId, int policy);
-    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT)")
     void reportVerificationIncomplete(int verificationId, int reason);
-    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT)")
-    void reportVerificationComplete(int verificationId, in VerificationStatus status);
-    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT)")
-    void reportVerificationCompleteWithExtensionResponse(int verificationId, in VerificationStatus status, in PersistableBundle response);
+    void reportVerificationComplete(int verificationId, in VerificationStatus status, in @nullable PersistableBundle extensionResponse);
 }
\ No newline at end of file
diff --git a/core/java/android/content/pm/verify/pkg/VerificationSession.java b/core/java/android/content/pm/verify/pkg/VerificationSession.java
index 4ade211..97f78e0 100644
--- a/core/java/android/content/pm/verify/pkg/VerificationSession.java
+++ b/core/java/android/content/pm/verify/pkg/VerificationSession.java
@@ -19,7 +19,6 @@
 import android.annotation.FlaggedApi;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
-import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.content.pm.Flags;
 import android.content.pm.PackageInstaller;
@@ -166,8 +165,8 @@
     /**
      * Get the value of Clock.elapsedRealtime() at which time this verification
      * will timeout as incomplete if no other verification response is provided.
+     * @throws SecurityException if the caller is not the current verifier bound by the system.
      */
-    @RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT)
     public long getTimeoutTime() {
         try {
             return mSession.getTimeoutTime(mId);
@@ -190,8 +189,8 @@
     /**
      * Override the verification policy for this session.
      * @return True if the override was successful, False otherwise.
+     * @throws SecurityException if the caller is not the current verifier bound by the system.
      */
-    @RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT)
     public boolean setVerificationPolicy(@PackageInstaller.VerificationPolicy int policy) {
         if (mVerificationPolicy == policy) {
             // No effective policy change
@@ -215,8 +214,8 @@
      * This may be called multiple times. If the request would bypass any max
      * duration by the system, the method will return a lower value than the
      * requested amount that indicates how much the time was extended.
+     * @throws SecurityException if the caller is not the current verifier bound by the system.
      */
-    @RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT)
     public long extendTimeRemaining(long additionalMs) {
         try {
             return mSession.extendTimeRemaining(mId, additionalMs);
@@ -227,9 +226,9 @@
 
     /**
      * Report to the system that verification could not be completed along
-     * with an approximate reason to pass on to the installer.
+     * with an approximate reason to pass on to the installer.]
+     * @throws SecurityException if the caller is not the current verifier bound by the system.
      */
-    @RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT)
     public void reportVerificationIncomplete(@VerificationIncompleteReason int reason) {
         try {
             mSession.reportVerificationIncomplete(mId, reason);
@@ -242,11 +241,11 @@
      * Report to the system that the verification has completed and the
      * install process may act on that status to either block in the case
      * of failure or continue to process the install in the case of success.
+     * @throws SecurityException if the caller is not the current verifier bound by the system.
      */
-    @RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT)
     public void reportVerificationComplete(@NonNull VerificationStatus status) {
         try {
-            mSession.reportVerificationComplete(mId, status);
+            mSession.reportVerificationComplete(mId, status,  /* extensionResponse= */ null);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -256,12 +255,12 @@
      * Same as {@link #reportVerificationComplete(VerificationStatus)}, but also provide
      * a result to the extension params provided in the request, which will be passed to the
      * installer in the installation result.
+     * @throws SecurityException if the caller is not the current verifier bound by the system.
      */
-    @RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT)
     public void reportVerificationComplete(@NonNull VerificationStatus status,
-            @NonNull PersistableBundle response) {
+            @NonNull PersistableBundle extensionResponse) {
         try {
-            mSession.reportVerificationCompleteWithExtensionResponse(mId, status, response);
+            mSession.reportVerificationComplete(mId, status, extensionResponse);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
diff --git a/core/java/android/database/BulkCursorNative.java b/core/java/android/database/BulkCursorNative.java
index 8ea450c..41585b3 100644
--- a/core/java/android/database/BulkCursorNative.java
+++ b/core/java/android/database/BulkCursorNative.java
@@ -53,7 +53,7 @@
 
         return new BulkCursorProxy(obj);
     }
-    
+
     @Override
     public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
             throws RemoteException {
@@ -79,7 +79,7 @@
                     reply.writeNoException();
                     return true;
                 }
-                
+
                 case CLOSE_TRANSACTION: {
                     data.enforceInterface(IBulkCursor.descriptor);
                     close();
@@ -212,15 +212,22 @@
         Parcel reply = Parcel.obtain();
         try {
             data.writeInterfaceToken(IBulkCursor.descriptor);
-
-            mRemote.transact(CLOSE_TRANSACTION, data, reply, 0);
-            DatabaseUtils.readExceptionFromParcel(reply);
+            // If close() is being called from the finalizer thread, do not wait for a reply from
+            // the remote side.
+            final boolean fromFinalizer =
+                    android.database.sqlite.Flags.onewayFinalizerClose()
+                    && "FinalizerDaemon".equals(Thread.currentThread().getName());
+            mRemote.transact(CLOSE_TRANSACTION, data, reply,
+                    fromFinalizer ? IBinder.FLAG_ONEWAY: 0);
+            if (!fromFinalizer) {
+                DatabaseUtils.readExceptionFromParcel(reply);
+            }
         } finally {
             data.recycle();
             reply.recycle();
         }
     }
-    
+
     public int requery(IContentObserver observer) throws RemoteException {
         Parcel data = Parcel.obtain();
         Parcel reply = Parcel.obtain();
@@ -282,4 +289,3 @@
         }
     }
 }
-
diff --git a/core/java/android/database/sqlite/flags.aconfig b/core/java/android/database/sqlite/flags.aconfig
index d5a7db8..826b908 100644
--- a/core/java/android/database/sqlite/flags.aconfig
+++ b/core/java/android/database/sqlite/flags.aconfig
@@ -2,6 +2,13 @@
 container: "system"
 
 flag {
+     name: "oneway_finalizer_close"
+     namespace: "system_performance"
+     description: "Make BuildCursorNative.close oneway if in the the finalizer"
+     bug: "368221351"
+}
+
+flag {
      name: "sqlite_apis_35"
      is_exported: true
      namespace: "system_performance"
diff --git a/core/java/android/hardware/biometrics/BiometricConstants.java b/core/java/android/hardware/biometrics/BiometricConstants.java
index 9355937..f649e47 100644
--- a/core/java/android/hardware/biometrics/BiometricConstants.java
+++ b/core/java/android/hardware/biometrics/BiometricConstants.java
@@ -164,15 +164,18 @@
     int BIOMETRIC_ERROR_POWER_PRESSED = 19;
 
     /**
-     * Mandatory biometrics is not in effect.
-     * @hide
+     * Identity Check is currently not active.
+     *
+     * This device either doesn't have this feature enabled, or it's not considered in a
+     * high-risk environment that requires extra security measures for accessing sensitive data.
      */
-    int BIOMETRIC_ERROR_MANDATORY_NOT_ACTIVE = 20;
+    @FlaggedApi(Flags.FLAG_IDENTITY_CHECK_API)
+    int BIOMETRIC_ERROR_IDENTITY_CHECK_NOT_ACTIVE = 20;
 
     /**
-     * Biometrics is not allowed to verify in apps.
-     * @hide
+     * Biometrics is not allowed to verify the user in apps.
      */
+    @FlaggedApi(Flags.FLAG_IDENTITY_CHECK_API)
     int BIOMETRIC_ERROR_NOT_ENABLED_FOR_APPS = 21;
 
     /**
@@ -204,6 +207,8 @@
             BIOMETRIC_ERROR_NEGATIVE_BUTTON,
             BIOMETRIC_ERROR_NO_DEVICE_CREDENTIAL,
             BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED,
+            BIOMETRIC_ERROR_IDENTITY_CHECK_NOT_ACTIVE,
+            BIOMETRIC_ERROR_NOT_ENABLED_FOR_APPS,
             BIOMETRIC_PAUSED_REJECTED})
     @Retention(RetentionPolicy.SOURCE)
     @interface Errors {}
diff --git a/core/java/android/hardware/biometrics/BiometricManager.java b/core/java/android/hardware/biometrics/BiometricManager.java
index a4f7485f..c690c67 100644
--- a/core/java/android/hardware/biometrics/BiometricManager.java
+++ b/core/java/android/hardware/biometrics/BiometricManager.java
@@ -87,16 +87,19 @@
             BiometricConstants.BIOMETRIC_ERROR_LOCKOUT;
 
     /**
-     * Mandatory biometrics is not effective.
-     * @hide
+     * Identity Check is currently not active.
+     *
+     * This device either doesn't have this feature enabled, or it's not considered in a
+     * high-risk environment that requires extra security measures for accessing sensitive data.
      */
-    public static final int BIOMETRIC_ERROR_MANDATORY_NOT_ACTIVE =
-            BiometricConstants.BIOMETRIC_ERROR_MANDATORY_NOT_ACTIVE;
+    @FlaggedApi(Flags.FLAG_IDENTITY_CHECK_API)
+    public static final int BIOMETRIC_ERROR_IDENTITY_CHECK_NOT_ACTIVE =
+            BiometricConstants.BIOMETRIC_ERROR_IDENTITY_CHECK_NOT_ACTIVE;
 
     /**
-     * Biometrics is not allowed to verify in apps.
-     * @hide
+     * Biometrics is not allowed to verify the user in apps.
      */
+    @FlaggedApi(Flags.FLAG_IDENTITY_CHECK_API)
     public static final int BIOMETRIC_ERROR_NOT_ENABLED_FOR_APPS =
             BiometricConstants.BIOMETRIC_ERROR_NOT_ENABLED_FOR_APPS;
 
@@ -136,7 +139,7 @@
             BIOMETRIC_ERROR_NO_HARDWARE,
             BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED,
             BIOMETRIC_ERROR_LOCKOUT,
-            BIOMETRIC_ERROR_MANDATORY_NOT_ACTIVE})
+            BIOMETRIC_ERROR_IDENTITY_CHECK_NOT_ACTIVE})
     @Retention(RetentionPolicy.SOURCE)
     public @interface BiometricError {}
 
@@ -160,7 +163,7 @@
                 BIOMETRIC_WEAK,
                 BIOMETRIC_CONVENIENCE,
                 DEVICE_CREDENTIAL,
-                MANDATORY_BIOMETRICS,
+                IDENTITY_CHECK,
         })
         @Retention(RetentionPolicy.SOURCE)
         @interface Types {}
@@ -239,20 +242,24 @@
         int DEVICE_CREDENTIAL = 1 << 15;
 
         /**
-         * The bit is used to request for mandatory biometrics.
+         * The bit is used to request for Identity Check.
          *
-         * <p> The requirements to trigger mandatory biometrics are as follows:
-         * 1. User must have enabled the toggle for mandatory biometrics is settings
-         * 2. User must have enrollments for all {@link #BIOMETRIC_STRONG} sensors available
-         * 3. The device must not be in a trusted location
+         * Identity Check is a feature which requires class 3 biometric authentication to access
+         * sensitive surfaces when the device is outside trusted places.
+         *
+         * <p> The requirements to trigger Identity Check are as follows:
+         * 1. User must have enabled the toggle for Identity Check in settings
+         * 2. User must have enrollments for at least one {@link #BIOMETRIC_STRONG} sensor
+         * 3. The device is determined to be in a high risk environment, for example if it is
+         *    outside of the user's trusted locations or fails to meet similar conditions.
+         * 4. The Identity Check requirements bit must be true
          * </p>
          *
          * <p> If all the above conditions are satisfied, only {@link #BIOMETRIC_STRONG} sensors
          * will be eligible for authentication, and device credential fallback will be dropped.
-         * @hide
          */
-        int MANDATORY_BIOMETRICS = 1 << 16;
-
+        @FlaggedApi(Flags.FLAG_IDENTITY_CHECK_API)
+        int IDENTITY_CHECK = 1 << 16;
     }
 
     /**
diff --git a/core/java/android/hardware/biometrics/PromptInfo.java b/core/java/android/hardware/biometrics/PromptInfo.java
index df5d864..e23ffeb 100644
--- a/core/java/android/hardware/biometrics/PromptInfo.java
+++ b/core/java/android/hardware/biometrics/PromptInfo.java
@@ -199,7 +199,7 @@
         } else if (mContentView != null && isContentViewMoreOptionsButtonUsed()) {
             return true;
         } else if (Flags.mandatoryBiometrics()
-                && (mAuthenticators & BiometricManager.Authenticators.MANDATORY_BIOMETRICS)
+                && (mAuthenticators & BiometricManager.Authenticators.IDENTITY_CHECK)
                 != 0) {
             return true;
         }
diff --git a/core/java/android/hardware/biometrics/flags.aconfig b/core/java/android/hardware/biometrics/flags.aconfig
index 26ffa11..52a4898 100644
--- a/core/java/android/hardware/biometrics/flags.aconfig
+++ b/core/java/android/hardware/biometrics/flags.aconfig
@@ -47,3 +47,10 @@
   description: "This flag controls Whether to enable fp unlock when screen turns off on udfps devices"
   bug: "373792870"
 }
+
+flag {
+  name: "identity_check_api"
+  namespace: "biometrics_framework"
+  description: "This flag is for API changes related to Identity Check"
+  bug: "373424727"
+}
diff --git a/core/java/android/hardware/camera2/params/OutputConfiguration.java b/core/java/android/hardware/camera2/params/OutputConfiguration.java
index ebcc371..22dbf5b 100644
--- a/core/java/android/hardware/camera2/params/OutputConfiguration.java
+++ b/core/java/android/hardware/camera2/params/OutputConfiguration.java
@@ -43,6 +43,7 @@
 import android.media.ImageReader;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.util.IntArray;
 import android.util.Log;
 import android.util.Size;
 import android.view.Surface;
@@ -596,6 +597,10 @@
         mStreamUseCase = CameraMetadata.SCALER_AVAILABLE_STREAM_USE_CASES_DEFAULT;
         mTimestampBase = TIMESTAMP_BASE_DEFAULT;
         mMirrorMode = MIRROR_MODE_AUTO;
+        mMirrorModeForSurfaces = new IntArray();
+        if (Flags.mirrorModeSharedSurfaces()) {
+            mMirrorModeForSurfaces.add(mMirrorMode);
+        }
         mReadoutTimestampEnabled = false;
         mIsReadoutSensorTimestampBase = false;
         mUsage = 0;
@@ -827,6 +832,7 @@
 
         mSurfaceGroupId = SURFACE_GROUP_ID_NONE;
         mSurfaces = new ArrayList<Surface>();
+        mMirrorModeForSurfaces = new IntArray();
         mRotation = ROTATION_0;
         mConfiguredSize = surfaceSize;
         mConfiguredFormat = StreamConfigurationMap.imageFormatToInternal(ImageFormat.PRIVATE);
@@ -971,6 +977,9 @@
         mDynamicRangeProfile = DynamicRangeProfiles.STANDARD;
         mColorSpace = ColorSpaceProfiles.UNSPECIFIED;
         mStreamUseCase = CameraMetadata.SCALER_AVAILABLE_STREAM_USE_CASES_DEFAULT;
+        mTimestampBase = TIMESTAMP_BASE_DEFAULT;
+        mMirrorMode = MIRROR_MODE_AUTO;
+        mMirrorModeForSurfaces = new IntArray();
         mReadoutTimestampEnabled = false;
         mIsReadoutSensorTimestampBase = false;
         mUsage = usage;
@@ -1239,6 +1248,9 @@
         }
 
         mSurfaces.add(surface);
+        if (Flags.mirrorModeSharedSurfaces()) {
+            mMirrorModeForSurfaces.add(mMirrorMode);
+        }
     }
 
     /**
@@ -1266,9 +1278,16 @@
             throw new IllegalArgumentException(
                     "Cannot remove surface associated with this output configuration");
         }
-        if (!mSurfaces.remove(surface)) {
+
+        int surfaceIndex = mSurfaces.indexOf(surface);
+        if (surfaceIndex == -1) {
             throw new IllegalArgumentException("Surface is not part of this output configuration");
         }
+
+        mSurfaces.remove(surfaceIndex);
+        if (Flags.mirrorModeSharedSurfaces()) {
+            mMirrorModeForSurfaces.remove(surfaceIndex);
+        }
     }
 
     /**
@@ -1405,6 +1424,11 @@
      * ImageReader, MediaRecorder, or MediaCodec, the mirror mode has no effect. If mirroring is
      * needed for such outputs, the application needs to mirror the image buffers itself before
      * passing them onward.</p>
+     *
+     * <p>Starting from Android 16, this function sets the mirror modes for all of the output
+     * surfaces contained within this OutputConfiguration. To set the mirror mode for a particular
+     * output surface, the application can call {@link #setMirrorMode(Surface, int)}. Prior to
+     * Android 16, this function is only applicable if surface sharing is not enabled.</p>
      */
     public void setMirrorMode(@MirrorMode int mirrorMode) {
         // Verify that the value is in range
@@ -1413,6 +1437,9 @@
             throw new IllegalArgumentException("Not a valid mirror mode " + mirrorMode);
         }
         mMirrorMode = mirrorMode;
+        for (int j = 0; j < mMirrorModeForSurfaces.size(); j++) {
+            mMirrorModeForSurfaces.set(j, mirrorMode);
+        }
     }
 
     /**
@@ -1428,6 +1455,72 @@
     }
 
     /**
+     * Set the mirroring mode for a surface belonging to this OutputConfiguration
+     *
+     * <p>This function is identical to {@link #setMirroMode(int)} if {@code surface} is
+     * the only surface belonging to this OutputConfiguration.</p>
+     *
+     * <p>If this OutputConfiguration contains a deferred surface, the application can either
+     * call {@link #setMirrorMode(int)}, or call this function after calling {@link #addSurface}.
+     * </p>
+     *
+     * <p>If this OutputConfiguration contains shared surfaces, the application can set
+     * different mirroring modes for different surfaces.</p>
+     *
+     * <p>For efficiency, the mirror effect is applied as a transform flag, so it is only effective
+     * in some outputs. It works automatically for SurfaceView and TextureView outputs. For manual
+     * use of SurfaceTexture, it is reflected in the value of
+     * {@link android.graphics.SurfaceTexture#getTransformMatrix}. For other end points, such as
+     * ImageReader, MediaRecorder, or MediaCodec, the mirror mode has no effect. If mirroring is
+     * needed for such outputs, the application needs to mirror the image buffers itself before
+     * passing them onward.</p>
+     *
+     * @throws IllegalArgumentException If the {@code surface} doesn't belong to this
+     *                                  OutputConfiguration, or the {@code mirrorMode} value is
+     *                                  not valid.
+     */
+    @FlaggedApi(Flags.FLAG_MIRROR_MODE_SHARED_SURFACES)
+    public void setMirrorMode(@NonNull Surface surface, @MirrorMode int mirrorMode) {
+        checkNotNull(surface, "Surface must not be null");
+        // Verify that the value is in range
+        if (mirrorMode < MIRROR_MODE_AUTO || mirrorMode > MIRROR_MODE_V) {
+            throw new IllegalArgumentException("Not a valid mirror mode " + mirrorMode);
+        }
+        int surfaceIndex = mSurfaces.indexOf(surface);
+        if (surfaceIndex == -1) {
+            throw new IllegalArgumentException("Surface not part of the OutputConfiguration");
+        }
+
+        mMirrorModeForSurfaces.set(surfaceIndex, mirrorMode);
+    }
+
+    /**
+     * Get the current mirroring mode for an output surface
+     *
+     * <p>If no {@link #setMirrorMode} is called first, this function returns
+     * {@link #MIRROR_MODE_AUTO}.</p>
+     *
+     * <p>If only {@link #setMirrorMode(int)} is called, the mirroring mode set by that
+     * function will be returned here as long as the {@code surface} belongs to this
+     * output configuration.</p>
+     *
+     * @throws IllegalArgumentException If the {@code surface} doesn't belong to this
+     *                                  OutputConfiguration.
+     *
+     * @return The mirroring mode for the specified output surface
+     */
+    @FlaggedApi(Flags.FLAG_MIRROR_MODE_SHARED_SURFACES)
+    public @MirrorMode int getMirrorMode(@NonNull Surface surface) {
+        checkNotNull(surface, "Surface must not be null");
+
+        int surfaceIndex = mSurfaces.indexOf(surface);
+        if (surfaceIndex == -1) {
+            throw new IllegalArgumentException("Surface not part of the OutputConfiguration");
+        }
+        return mMirrorModeForSurfaces.get(surfaceIndex);
+    }
+
+    /**
      * Use the camera sensor's readout time for the image timestamp.
      *
      * <p>The start of the camera sensor readout after exposure. For a rolling shutter camera
@@ -1491,6 +1584,7 @@
         this.mStreamUseCase = other.mStreamUseCase;
         this.mTimestampBase = other.mTimestampBase;
         this.mMirrorMode = other.mMirrorMode;
+        this.mMirrorModeForSurfaces = other.mMirrorModeForSurfaces.clone();
         this.mReadoutTimestampEnabled = other.mReadoutTimestampEnabled;
         this.mUsage = other.mUsage;
     }
@@ -1520,6 +1614,7 @@
 
         int timestampBase = source.readInt();
         int mirrorMode = source.readInt();
+        int[] mirrorModeForSurfaces = source.createIntArray();
         boolean readoutTimestampEnabled = source.readInt() == 1;
         int format = source.readInt();
         int dataSpace = source.readInt();
@@ -1531,7 +1626,6 @@
         mConfiguredSize = new Size(width, height);
         mIsDeferredConfig = isDeferred;
         mIsShared = isShared;
-        mSurfaces = surfaces;
         mUsage = 0;
         if (mSurfaces.size() > 0) {
             mSurfaceType = SURFACE_TYPE_UNKNOWN;
@@ -1560,6 +1654,7 @@
         mStreamUseCase = streamUseCase;
         mTimestampBase = timestampBase;
         mMirrorMode = mirrorMode;
+        mMirrorModeForSurfaces = IntArray.wrap(mirrorModeForSurfaces);
         mReadoutTimestampEnabled = readoutTimestampEnabled;
     }
 
@@ -1706,6 +1801,7 @@
         dest.writeLong(mStreamUseCase);
         dest.writeInt(mTimestampBase);
         dest.writeInt(mMirrorMode);
+        dest.writeIntArray(mMirrorModeForSurfaces.toArray());
         dest.writeInt(mReadoutTimestampEnabled ? 1 : 0);
         dest.writeInt(mConfiguredFormat);
         dest.writeInt(mConfiguredDataspace);
@@ -1756,6 +1852,16 @@
                     return false;
                 }
             }
+            if (Flags.mirrorModeSharedSurfaces()) {
+                if (mMirrorModeForSurfaces.size() != other.mMirrorModeForSurfaces.size()) {
+                    return false;
+                }
+                for (int j = 0; j < mMirrorModeForSurfaces.size(); j++) {
+                    if (mMirrorModeForSurfaces.get(j) != other.mMirrorModeForSurfaces.get(j)) {
+                        return false;
+                    }
+                }
+            }
             int minLen = Math.min(mSurfaces.size(), other.mSurfaces.size());
             for (int i = 0;  i < minLen; i++) {
                 if (mSurfaces.get(i) != other.mSurfaces.get(i))
@@ -1799,8 +1905,9 @@
                     mPhysicalCameraId == null ? 0 : mPhysicalCameraId.hashCode(),
                     mIsMultiResolution ? 1 : 0, mSensorPixelModesUsed.hashCode(),
                     mDynamicRangeProfile, mColorSpace, mStreamUseCase,
-                    mTimestampBase, mMirrorMode, mReadoutTimestampEnabled ? 1 : 0,
-                    Long.hashCode(mUsage));
+                    mTimestampBase, mMirrorMode,
+                    HashCodeHelpers.hashCode(mMirrorModeForSurfaces.toArray()),
+                    mReadoutTimestampEnabled ? 1 : 0, Long.hashCode(mUsage));
         }
 
         return HashCodeHelpers.hashCode(
@@ -1810,7 +1917,9 @@
                 mPhysicalCameraId == null ? 0 : mPhysicalCameraId.hashCode(),
                 mIsMultiResolution ? 1 : 0, mSensorPixelModesUsed.hashCode(),
                 mDynamicRangeProfile, mColorSpace, mStreamUseCase, mTimestampBase,
-                mMirrorMode, mReadoutTimestampEnabled ? 1 : 0, Long.hashCode(mUsage));
+                mMirrorMode, HashCodeHelpers.hashCode(mMirrorModeForSurfaces.toArray()),
+                mReadoutTimestampEnabled ? 1 : 0,
+                Long.hashCode(mUsage));
     }
 
     private static final String TAG = "OutputConfiguration";
@@ -1852,6 +1961,8 @@
     private int mTimestampBase;
     // Mirroring mode
     private int mMirrorMode;
+    // Per-surface mirror modes
+    private IntArray mMirrorModeForSurfaces;
     // readout timestamp
     private boolean mReadoutTimestampEnabled;
     // Whether the timestamp base is set to READOUT_SENSOR
diff --git a/core/java/android/hardware/display/AmbientDisplayConfiguration.java b/core/java/android/hardware/display/AmbientDisplayConfiguration.java
index 59a602ca..7361d4f 100644
--- a/core/java/android/hardware/display/AmbientDisplayConfiguration.java
+++ b/core/java/android/hardware/display/AmbientDisplayConfiguration.java
@@ -16,6 +16,8 @@
 
 package android.hardware.display;
 
+import static android.provider.Settings.Secure.SCREEN_OFF_UNLOCK_UDFPS_ENABLED;
+
 import android.annotation.TestApi;
 import android.content.Context;
 import android.hardware.biometrics.Flags;
@@ -151,8 +153,8 @@
     public boolean screenOffUdfpsEnabled(int user) {
         return !TextUtils.isEmpty(udfpsLongPressSensorType())
                 && ((mScreenOffUdfpsEnabledByDefault && Flags.screenOffUnlockUdfps())
-                ? boolSettingDefaultOn("screen_off_udfps_enabled", user)
-                : boolSettingDefaultOff("screen_off_udfps_enabled", user));
+                ? boolSettingDefaultOn(SCREEN_OFF_UNLOCK_UDFPS_ENABLED, user)
+                : boolSettingDefaultOff(SCREEN_OFF_UNLOCK_UDFPS_ENABLED, user));
     }
 
     /** @hide */
diff --git a/core/java/android/hardware/input/AidlInputGestureData.aidl b/core/java/android/hardware/input/AidlInputGestureData.aidl
new file mode 100644
index 0000000..137f672
--- /dev/null
+++ b/core/java/android/hardware/input/AidlInputGestureData.aidl
@@ -0,0 +1,31 @@
+/*
+ * Copyright 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.input;
+
+/** @hide */
+@JavaDerive(equals=true)
+parcelable AidlInputGestureData {
+    int keycode;
+    int modifierState;
+    int gestureType;
+
+    // App launch parameters: Only set if gestureType is KEY_GESTURE_TYPE_LAUNCH_APPLICATION
+    String appLaunchCategory;
+    String appLaunchRole;
+    String appLaunchPackageName;
+    String appLaunchClassName;
+}
diff --git a/core/java/android/hardware/input/AidlKeyGestureEvent.aidl b/core/java/android/hardware/input/AidlKeyGestureEvent.aidl
index 7cf8795..fc71519 100644
--- a/core/java/android/hardware/input/AidlKeyGestureEvent.aidl
+++ b/core/java/android/hardware/input/AidlKeyGestureEvent.aidl
@@ -26,4 +26,10 @@
     int action;
     int displayId;
     int flags;
+
+    // App launch parameters: only set when gestureType = KEY_GESTURE_TYPE_LAUNCH_APPLICATION
+    String appLaunchCategory;
+    String appLaunchRole;
+    String appLaunchPackageName;
+    String appLaunchClassName;
 }
diff --git a/core/java/android/hardware/input/AppLaunchData.java b/core/java/android/hardware/input/AppLaunchData.java
new file mode 100644
index 0000000..43186f5
--- /dev/null
+++ b/core/java/android/hardware/input/AppLaunchData.java
@@ -0,0 +1,176 @@
+/*
+ * Copyright 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.input;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.text.TextUtils;
+
+import java.util.Objects;
+
+/**
+ * Provides data for launching an application.
+ *
+ * @hide
+ */
+public interface AppLaunchData {
+
+    /** Creates AppLaunchData for the provided category */
+    @NonNull
+    static AppLaunchData createLaunchDataForCategory(@NonNull String category) {
+        return new CategoryData(category);
+    }
+
+    /** Creates AppLaunchData for the provided role */
+    @NonNull
+    static AppLaunchData createLaunchDataForRole(@NonNull String role) {
+        return new RoleData(role);
+    }
+
+    /** Creates AppLaunchData for the target package name and class name */
+    @NonNull
+    static AppLaunchData createLaunchDataForComponent(@NonNull String packageName,
+            @NonNull String className) {
+        return new ComponentData(packageName, className);
+    }
+
+    @Nullable
+    static AppLaunchData createLaunchData(@Nullable String category, @Nullable String role,
+            @Nullable String packageName, @Nullable String className) {
+        if (!TextUtils.isEmpty(category)) {
+            return new CategoryData(category);
+        }
+        if (!TextUtils.isEmpty(role)) {
+            return new RoleData(role);
+        }
+        if (!TextUtils.isEmpty(packageName) && !TextUtils.isEmpty(className)) {
+            return new ComponentData(packageName, className);
+        }
+        return null;
+    }
+
+    /** Intent category based app launch data */
+    class CategoryData implements AppLaunchData {
+        @NonNull
+        private final String mCategory;
+        public CategoryData(@NonNull String category) {
+            mCategory = category;
+        }
+
+        @NonNull
+        public String getCategory() {
+            return mCategory;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (!(o instanceof CategoryData that)) return false;
+            return Objects.equals(mCategory, that.mCategory);
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(mCategory);
+        }
+
+        @Override
+        public String toString() {
+            return "CategoryData{" +
+                    "mCategory='" + mCategory + '\'' +
+                    '}';
+        }
+    }
+
+    /** Role based app launch data */
+    class RoleData implements AppLaunchData {
+        @NonNull
+        private final String mRole;
+        public RoleData(@NonNull String role) {
+            mRole = role;
+        }
+
+        @NonNull
+        public String getRole() {
+            return mRole;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (!(o instanceof RoleData roleData)) return false;
+            return Objects.equals(mRole, roleData.mRole);
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(mRole);
+        }
+
+        @Override
+        public String toString() {
+            return "RoleData{" +
+                    "mRole='" + mRole + '\'' +
+                    '}';
+        }
+    }
+
+    /** Target application launch data */
+    class ComponentData implements AppLaunchData {
+        @NonNull
+        private final String mPackageName;
+
+        @NonNull
+        private final String mClassName;
+
+        public ComponentData(@NonNull String packageName, @NonNull String className) {
+            mPackageName = packageName;
+            mClassName = className;
+        }
+
+        @NonNull
+        public String getPackageName() {
+            return mPackageName;
+        }
+
+        @NonNull
+        public String getClassName() {
+            return mClassName;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (!(o instanceof ComponentData that)) return false;
+            return Objects.equals(mPackageName, that.mPackageName) && Objects.equals(
+                    mClassName, that.mClassName);
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(mPackageName, mClassName);
+        }
+
+        @Override
+        public String toString() {
+            return "ComponentData{" +
+                    "mPackageName='" + mPackageName + '\'' +
+                    ", mClassName='" + mClassName + '\'' +
+                    '}';
+        }
+    }
+}
diff --git a/core/java/android/hardware/input/IInputManager.aidl b/core/java/android/hardware/input/IInputManager.aidl
index 102f56e..bce9518 100644
--- a/core/java/android/hardware/input/IInputManager.aidl
+++ b/core/java/android/hardware/input/IInputManager.aidl
@@ -17,6 +17,7 @@
 package android.hardware.input;
 
 import android.graphics.Rect;
+import android.hardware.input.AidlInputGestureData;
 import android.hardware.input.HostUsiVersion;
 import android.hardware.input.InputDeviceIdentifier;
 import android.hardware.input.KeyboardLayout;
@@ -261,4 +262,21 @@
     @JavaPassthrough(annotation="@android.annotation.RequiresPermission(value = "
             + "android.Manifest.permission.MANAGE_KEY_GESTURES)")
     void unregisterKeyGestureHandler(IKeyGestureHandler handler);
+
+    @PermissionManuallyEnforced
+    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(value = "
+            + "android.Manifest.permission.MANAGE_KEY_GESTURES)")
+    int addCustomInputGesture(in AidlInputGestureData data);
+
+    @PermissionManuallyEnforced
+    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(value = "
+            + "android.Manifest.permission.MANAGE_KEY_GESTURES)")
+    int removeCustomInputGesture(in AidlInputGestureData data);
+
+    @PermissionManuallyEnforced
+    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(value = "
+            + "android.Manifest.permission.MANAGE_KEY_GESTURES)")
+    void removeAllCustomInputGestures();
+
+    AidlInputGestureData[] getCustomInputGestures();
 }
diff --git a/core/java/android/hardware/input/InputGestureData.java b/core/java/android/hardware/input/InputGestureData.java
new file mode 100644
index 0000000..5ab73ce
--- /dev/null
+++ b/core/java/android/hardware/input/InputGestureData.java
@@ -0,0 +1,249 @@
+/*
+ * Copyright 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.input;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.view.KeyEvent;
+
+import java.util.Objects;
+
+/**
+ * Data class to store input gesture data.
+ *
+ * <p>
+ * All input gestures are of type Trigger -> Action(Key gesture type, app data). And currently types
+ * of triggers supported are:
+ * - KeyTrigger (Keycode + modifierState)
+ * - TODO(b/365064144): Add Touchpad gesture based trigger
+ * </p>
+ * @hide
+ */
+public final class InputGestureData {
+
+    @NonNull
+    private final AidlInputGestureData mInputGestureData;
+
+    public InputGestureData(AidlInputGestureData inputGestureData) {
+        this.mInputGestureData = inputGestureData;
+        validate();
+    }
+
+    /** Returns the trigger information for this input gesture */
+    public Trigger getTrigger() {
+        if (mInputGestureData.keycode != KeyEvent.KEYCODE_UNKNOWN) {
+            return new KeyTrigger(mInputGestureData.keycode, mInputGestureData.modifierState);
+        }
+        throw new RuntimeException("InputGestureData is corrupted, invalid trigger type!");
+    }
+
+    /** Returns the action to perform for this input gesture */
+    public Action getAction() {
+        return new Action(mInputGestureData.gestureType, getAppLaunchData());
+    }
+
+    private void validate() {
+        Trigger trigger = getTrigger();
+        Action action = getAction();
+        if (trigger == null) {
+            throw new IllegalArgumentException("No trigger found");
+        }
+        if (action.keyGestureType == KeyGestureEvent.KEY_GESTURE_TYPE_UNSPECIFIED) {
+            throw new IllegalArgumentException("No system action found");
+        }
+        if (action.keyGestureType == KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION
+                && action.appLaunchData == null) {
+            throw new IllegalArgumentException(
+                    "No app launch data for system action launch application");
+        }
+    }
+
+    public AidlInputGestureData getAidlData() {
+        return mInputGestureData;
+    }
+
+    @Nullable
+    private AppLaunchData getAppLaunchData() {
+        if (mInputGestureData.gestureType != KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION) {
+            return null;
+        }
+        return AppLaunchData.createLaunchData(mInputGestureData.appLaunchCategory,
+                mInputGestureData.appLaunchRole, mInputGestureData.appLaunchPackageName,
+                mInputGestureData.appLaunchClassName);
+    }
+
+    /** Builder class for creating {@link InputGestureData} */
+    public static class Builder {
+        @Nullable
+        private Trigger mTrigger = null;
+        private int mKeyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_UNSPECIFIED;
+        @Nullable
+        private AppLaunchData mAppLaunchData = null;
+
+        /** Set input gesture trigger data for key based gestures */
+        public Builder setTrigger(Trigger trigger) {
+            mTrigger = trigger;
+            return this;
+        }
+
+        /** Set input gesture system action */
+        public Builder setKeyGestureType(@KeyGestureEvent.KeyGestureType int keyGestureType) {
+            mKeyGestureType = keyGestureType;
+            return this;
+        }
+
+        /** Set input gesture system action as launching a target app */
+        public Builder setAppLaunchData(@NonNull AppLaunchData appLaunchData) {
+            mKeyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION;
+            mAppLaunchData = appLaunchData;
+            return this;
+        }
+
+        /** Creates {@link android.hardware.input.InputGestureData} based on data provided */
+        public InputGestureData build() throws IllegalArgumentException {
+            if (mTrigger == null) {
+                throw new IllegalArgumentException("No trigger found");
+            }
+            if (mKeyGestureType == KeyGestureEvent.KEY_GESTURE_TYPE_UNSPECIFIED) {
+                throw new IllegalArgumentException("No system action found");
+            }
+            if (mKeyGestureType == KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION
+                    && mAppLaunchData == null) {
+                throw new IllegalArgumentException(
+                        "No app launch data for system action launch application");
+            }
+            AidlInputGestureData data = new AidlInputGestureData();
+            if (mTrigger instanceof KeyTrigger keyTrigger) {
+                data.keycode = keyTrigger.getKeycode();
+                data.modifierState = keyTrigger.getModifierState();
+            } else {
+                throw new IllegalArgumentException("Invalid trigger type!");
+            }
+            data.gestureType = mKeyGestureType;
+            if (mAppLaunchData != null) {
+                if (mAppLaunchData instanceof AppLaunchData.CategoryData categoryData) {
+                    data.appLaunchCategory = categoryData.getCategory();
+                } else if (mAppLaunchData instanceof AppLaunchData.RoleData roleData) {
+                    data.appLaunchRole = roleData.getRole();
+                } else if (mAppLaunchData instanceof AppLaunchData.ComponentData componentData) {
+                    data.appLaunchPackageName = componentData.getPackageName();
+                    data.appLaunchClassName = componentData.getClassName();
+                } else {
+                    throw new IllegalArgumentException("AppLaunchData type is invalid!");
+                }
+            }
+            return new InputGestureData(data);
+        }
+    }
+
+    @Override
+    public String toString() {
+        return "InputGestureData { "
+                + "trigger = " + getTrigger()
+                + ", action = " + getAction()
+                + " }";
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        InputGestureData that = (InputGestureData) o;
+        return mInputGestureData.keycode == that.mInputGestureData.keycode
+                && mInputGestureData.modifierState == that.mInputGestureData.modifierState
+                && mInputGestureData.gestureType == that.mInputGestureData.gestureType
+                && Objects.equals(mInputGestureData.appLaunchCategory, that.mInputGestureData.appLaunchCategory)
+                && Objects.equals(mInputGestureData.appLaunchRole, that.mInputGestureData.appLaunchRole)
+                && Objects.equals(mInputGestureData.appLaunchPackageName, that.mInputGestureData.appLaunchPackageName)
+                && Objects.equals(mInputGestureData.appLaunchPackageName, that.mInputGestureData.appLaunchPackageName);
+    }
+
+    @Override
+    public int hashCode() {
+        int _hash = 1;
+        _hash = 31 * _hash + mInputGestureData.keycode;
+        _hash = 31 * _hash + mInputGestureData.modifierState;
+        _hash = 31 * _hash + mInputGestureData.gestureType;
+        _hash = 31 * _hash + (mInputGestureData.appLaunchCategory != null
+                ? mInputGestureData.appLaunchCategory.hashCode() : 0);
+        _hash = 31 * _hash + (mInputGestureData.appLaunchRole != null
+                ? mInputGestureData.appLaunchRole.hashCode() : 0);
+        _hash = 31 * _hash + (mInputGestureData.appLaunchPackageName != null
+                ? mInputGestureData.appLaunchPackageName.hashCode() : 0);
+        _hash = 31 * _hash + (mInputGestureData.appLaunchPackageName != null
+                ? mInputGestureData.appLaunchPackageName.hashCode() : 0);
+        return _hash;
+    }
+
+    public interface Trigger {
+    }
+
+    /** Creates a input gesture trigger based on a key press */
+    public static Trigger createKeyTrigger(int keycode, int modifierState) {
+        return new KeyTrigger(keycode, modifierState);
+    }
+
+    /** Key based input gesture trigger */
+    public static class KeyTrigger implements Trigger {
+        private static final int SHORTCUT_META_MASK =
+                KeyEvent.META_META_ON | KeyEvent.META_CTRL_ON | KeyEvent.META_ALT_ON
+                        | KeyEvent.META_SHIFT_ON;
+        private final int mKeycode;
+        private final int mModifierState;
+
+        private KeyTrigger(int keycode, int modifierState) {
+            if (keycode <= KeyEvent.KEYCODE_UNKNOWN || keycode > KeyEvent.getMaxKeyCode()) {
+                throw new IllegalArgumentException("Invalid keycode = " + keycode);
+            }
+            mKeycode = keycode;
+            mModifierState = modifierState;
+        }
+
+        public int getKeycode() {
+            return mKeycode;
+        }
+
+        public int getModifierState() {
+            return mModifierState;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (!(o instanceof KeyTrigger that)) return false;
+            return mKeycode == that.mKeycode && mModifierState == that.mModifierState;
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(mKeycode, mModifierState);
+        }
+
+        @Override
+        public String toString() {
+            return "KeyTrigger{" +
+                    "mKeycode=" + KeyEvent.keyCodeToString(mKeycode) +
+                    ", mModifierState=" + mModifierState +
+                    '}';
+        }
+    }
+
+    /** Data for action to perform when input gesture is triggered */
+    public record Action(@KeyGestureEvent.KeyGestureType int keyGestureType,
+                         @Nullable AppLaunchData appLaunchData) {
+    }
+}
diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java
index 22728f7..876ba10 100644
--- a/core/java/android/hardware/input/InputManager.java
+++ b/core/java/android/hardware/input/InputManager.java
@@ -18,6 +18,7 @@
 
 import static com.android.input.flags.Flags.FLAG_INPUT_DEVICE_VIEW_BEHAVIOR_API;
 import static com.android.input.flags.Flags.FLAG_DEVICE_ASSOCIATIONS;
+import static com.android.hardware.input.Flags.enableCustomizableInputGestures;
 import static com.android.hardware.input.Flags.keyboardLayoutPreviewFlag;
 import static com.android.hardware.input.Flags.keyboardGlyphMap;
 
@@ -258,6 +259,52 @@
     }
 
     /**
+     * Custom input gesture error: Input gesture already exists
+     *
+     * @hide
+     */
+    public static final int CUSTOM_INPUT_GESTURE_RESULT_SUCCESS = 1;
+
+    /**
+     * Custom input gesture error: Input gesture already exists
+     *
+     * @hide
+     */
+    public static final int CUSTOM_INPUT_GESTURE_RESULT_ERROR_ALREADY_EXISTS = 2;
+
+    /**
+     * Custom input gesture error: Input gesture does not exist
+     *
+     * @hide
+     */
+    public static final int CUSTOM_INPUT_GESTURE_RESULT_ERROR_DOES_NOT_EXIST = 3;
+
+    /**
+     * Custom input gesture error: Input gesture is reserved for system action
+     *
+     * @hide
+     */
+    public static final int CUSTOM_INPUT_GESTURE_RESULT_ERROR_RESERVED_GESTURE = 4;
+
+    /**
+     * Custom input gesture error: Failure error code for all other errors/warnings
+     *
+     * @hide
+     */
+    public static final int CUSTOM_INPUT_GESTURE_RESULT_ERROR_OTHER = 5;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = { "CUSTOM_INPUT_GESTURE_RESULT_" }, value = {
+            CUSTOM_INPUT_GESTURE_RESULT_SUCCESS,
+            CUSTOM_INPUT_GESTURE_RESULT_ERROR_ALREADY_EXISTS,
+            CUSTOM_INPUT_GESTURE_RESULT_ERROR_DOES_NOT_EXIST,
+            CUSTOM_INPUT_GESTURE_RESULT_ERROR_RESERVED_GESTURE,
+            CUSTOM_INPUT_GESTURE_RESULT_ERROR_OTHER,
+    })
+    public @interface CustomInputGestureResult {}
+
+    /**
      * Switch State: Unknown.
      *
      * The system has yet to report a valid value for the switch.
@@ -1432,6 +1479,84 @@
         mGlobal.unregisterKeyGestureEventHandler(handler);
     }
 
+    /** Adds a new custom input gesture
+     *
+     * @param inputGestureData gesture data to add as custom gesture
+     *
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.MANAGE_KEY_GESTURES)
+    @CustomInputGestureResult
+    public int addCustomInputGesture(@NonNull InputGestureData inputGestureData) {
+        if (!enableCustomizableInputGestures()) {
+            return CUSTOM_INPUT_GESTURE_RESULT_ERROR_OTHER;
+        }
+        try {
+            return mIm.addCustomInputGesture(inputGestureData.getAidlData());
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+        return CUSTOM_INPUT_GESTURE_RESULT_ERROR_OTHER;
+    }
+
+    /** Removes an existing custom gesture
+     *
+     * <p> NOTE: Should not be used to remove system gestures. This API is only to be used to
+     * remove gestures added using {@link #addCustomInputGesture(InputGestureData)}
+     *
+     * @param inputGestureData gesture data for the existing custom gesture to remove
+     *
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.MANAGE_KEY_GESTURES)
+    @CustomInputGestureResult
+    public int removeCustomInputGesture(@NonNull InputGestureData inputGestureData) {
+        if (!enableCustomizableInputGestures()) {
+            return CUSTOM_INPUT_GESTURE_RESULT_ERROR_OTHER;
+        }
+        try {
+            return mIm.removeCustomInputGesture(inputGestureData.getAidlData());
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+        return CUSTOM_INPUT_GESTURE_RESULT_ERROR_OTHER;
+    }
+
+    /** Removes all custom input gestures
+     *
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.MANAGE_KEY_GESTURES)
+    public void removeAllCustomInputGestures() {
+        if (!enableCustomizableInputGestures()) {
+            return;
+        }
+        try {
+            mIm.removeAllCustomInputGestures();
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    /** Get all custom input gestures
+     *
+     * @hide
+     */
+    public List<InputGestureData> getCustomInputGestures() {
+        List<InputGestureData> result = new ArrayList<>();
+        if (!enableCustomizableInputGestures()) {
+            return result;
+        }
+        try {
+            for (AidlInputGestureData data : mIm.getCustomInputGestures()) {
+                result.add(new InputGestureData(data));
+            }
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+        return result;
+    }
+
     /**
      * A callback used to be notified about battery state changes for an input device. The
      * {@link #onBatteryStateChanged(int, long, BatteryState)} method will be called once after the
diff --git a/core/java/android/hardware/input/InputSettings.java b/core/java/android/hardware/input/InputSettings.java
index 897ce4a..4a9efe0 100644
--- a/core/java/android/hardware/input/InputSettings.java
+++ b/core/java/android/hardware/input/InputSettings.java
@@ -72,6 +72,20 @@
     public static final int DEFAULT_POINTER_SPEED = 0;
 
     /**
+     * Bounce Keys Threshold: The default value of the threshold (500 ms).
+     *
+     * @hide
+     */
+    public static final int DEFAULT_BOUNCE_KEYS_THRESHOLD_MILLIS = 500;
+
+    /**
+     * Slow Keys Threshold: The default value of the threshold (500 ms).
+     *
+     * @hide
+     */
+    public static final int DEFAULT_SLOW_KEYS_THRESHOLD_MILLIS = 500;
+
+    /**
      * The maximum allowed obscuring opacity by UID to propagate touches (0 <= x <= 1).
      * @hide
      */
diff --git a/core/java/android/hardware/input/KeyGestureEvent.java b/core/java/android/hardware/input/KeyGestureEvent.java
index 71d17eb..0f290d9 100644
--- a/core/java/android/hardware/input/KeyGestureEvent.java
+++ b/core/java/android/hardware/input/KeyGestureEvent.java
@@ -19,6 +19,8 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.app.role.RoleManager;
+import android.content.Intent;
 import android.view.Display;
 import android.view.KeyCharacterMap;
 
@@ -26,6 +28,7 @@
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
 
 /**
  * Provides information about the keyboard gesture event being triggered by an external keyboard.
@@ -37,6 +40,9 @@
     @NonNull
     private AidlKeyGestureEvent mKeyGestureEvent;
 
+    private static final int LOG_EVENT_UNSPECIFIED =
+            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__UNSPECIFIED;
+
     public static final int KEY_GESTURE_TYPE_UNSPECIFIED = 0;
     public static final int KEY_GESTURE_TYPE_HOME = 1;
     public static final int KEY_GESTURE_TYPE_RECENT_APPS = 2;
@@ -76,6 +82,8 @@
     public static final int KEY_GESTURE_TYPE_SLEEP = 36;
     public static final int KEY_GESTURE_TYPE_WAKEUP = 37;
     public static final int KEY_GESTURE_TYPE_MEDIA_KEY = 38;
+    // TODO(b/280423320): Remove "LAUNCH_DEFAULT_..." gestures and rely on launch intent to find
+    //  the correct logging event.
     public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_BROWSER = 39;
     public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_EMAIL = 40;
     public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CONTACTS = 41;
@@ -88,7 +96,7 @@
     public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_FILES = 48;
     public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_WEATHER = 49;
     public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_FITNESS = 50;
-    public static final int KEY_GESTURE_TYPE_LAUNCH_APPLICATION_BY_PACKAGE_NAME = 51;
+    public static final int KEY_GESTURE_TYPE_LAUNCH_APPLICATION = 51;
     public static final int KEY_GESTURE_TYPE_DESKTOP_MODE = 52;
     public static final int KEY_GESTURE_TYPE_MULTI_WINDOW_NAVIGATION = 53;
     public static final int KEY_GESTURE_TYPE_RECENT_APPS_SWITCHER = 54;
@@ -101,6 +109,10 @@
     public static final int KEY_GESTURE_TYPE_CLOSE_ALL_DIALOGS = 61;
     public static final int KEY_GESTURE_TYPE_MOVE_TO_NEXT_DISPLAY = 62;
     public static final int KEY_GESTURE_TYPE_TOGGLE_TALKBACK = 63;
+    public static final int KEY_GESTURE_TYPE_TOGGLE_STICKY_KEYS = 64;
+    public static final int KEY_GESTURE_TYPE_TOGGLE_BOUNCE_KEYS = 65;
+    public static final int KEY_GESTURE_TYPE_TOGGLE_SLOW_KEYS = 66;
+    public static final int KEY_GESTURE_TYPE_TOGGLE_MOUSE_KEYS = 67;
 
     public static final int FLAG_CANCELLED = 1;
 
@@ -166,7 +178,7 @@
             KEY_GESTURE_TYPE_LAUNCH_DEFAULT_FILES,
             KEY_GESTURE_TYPE_LAUNCH_DEFAULT_WEATHER,
             KEY_GESTURE_TYPE_LAUNCH_DEFAULT_FITNESS,
-            KEY_GESTURE_TYPE_LAUNCH_APPLICATION_BY_PACKAGE_NAME,
+            KEY_GESTURE_TYPE_LAUNCH_APPLICATION,
             KEY_GESTURE_TYPE_DESKTOP_MODE,
             KEY_GESTURE_TYPE_MULTI_WINDOW_NAVIGATION,
             KEY_GESTURE_TYPE_RECENT_APPS_SWITCHER,
@@ -179,6 +191,10 @@
             KEY_GESTURE_TYPE_CLOSE_ALL_DIALOGS,
             KEY_GESTURE_TYPE_MOVE_TO_NEXT_DISPLAY,
             KEY_GESTURE_TYPE_TOGGLE_TALKBACK,
+            KEY_GESTURE_TYPE_TOGGLE_STICKY_KEYS,
+            KEY_GESTURE_TYPE_TOGGLE_BOUNCE_KEYS,
+            KEY_GESTURE_TYPE_TOGGLE_SLOW_KEYS,
+            KEY_GESTURE_TYPE_TOGGLE_MOUSE_KEYS
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface KeyGestureType {
@@ -210,6 +226,8 @@
         private int mAction = KeyGestureEvent.ACTION_GESTURE_COMPLETE;
         private int mDisplayId = Display.DEFAULT_DISPLAY;
         private int mFlags = 0;
+        @Nullable
+        private AppLaunchData mAppLaunchData = null;
 
         /**
          * @see KeyGestureEvent#getDeviceId()
@@ -268,6 +286,14 @@
         }
 
         /**
+         * @see KeyGestureEvent#getAppLaunchData()
+         */
+        public Builder setAppLaunchData(@NonNull AppLaunchData appLaunchData) {
+            mAppLaunchData = appLaunchData;
+            return this;
+        }
+
+        /**
          * Build {@link KeyGestureEvent}
          */
         public KeyGestureEvent build() {
@@ -279,6 +305,21 @@
             event.action = mAction;
             event.displayId = mDisplayId;
             event.flags = mFlags;
+            if (mAppLaunchData != null) {
+                if (mAppLaunchData instanceof AppLaunchData.CategoryData) {
+                    event.appLaunchCategory =
+                            ((AppLaunchData.CategoryData) mAppLaunchData).getCategory();
+                } else if (mAppLaunchData instanceof AppLaunchData.RoleData) {
+                    event.appLaunchRole = ((AppLaunchData.RoleData) mAppLaunchData).getRole();
+                } else if (mAppLaunchData instanceof AppLaunchData.ComponentData) {
+                    event.appLaunchPackageName =
+                            ((AppLaunchData.ComponentData) mAppLaunchData).getPackageName();
+                    event.appLaunchClassName =
+                            ((AppLaunchData.ComponentData) mAppLaunchData).getClassName();
+                } else {
+                    throw new IllegalArgumentException("AppLaunchData type is invalid!");
+                }
+            }
             return new KeyGestureEvent(event);
         }
     }
@@ -315,6 +356,27 @@
         return (mKeyGestureEvent.flags & FLAG_CANCELLED) != 0;
     }
 
+    public int getLogEvent() {
+        if (getKeyGestureType() == KEY_GESTURE_TYPE_LAUNCH_APPLICATION) {
+            return getLogEventFromLaunchAppData(getAppLaunchData());
+        }
+        return keyGestureTypeToLogEvent(getKeyGestureType());
+    }
+
+    /**
+     * @return Launch app data associated with the event, only if key gesture type is
+     * {@code KEY_GESTURE_TYPE_LAUNCH_APPLICATION}
+     */
+    @Nullable
+    public AppLaunchData getAppLaunchData() {
+        if (mKeyGestureEvent.gestureType != KEY_GESTURE_TYPE_LAUNCH_APPLICATION) {
+            return null;
+        }
+        return AppLaunchData.createLaunchData(mKeyGestureEvent.appLaunchCategory,
+                mKeyGestureEvent.appLaunchRole, mKeyGestureEvent.appLaunchPackageName,
+                mKeyGestureEvent.appLaunchClassName);
+    }
+
     @Override
     public String toString() {
         return "KeyGestureEvent { "
@@ -324,7 +386,8 @@
                 + "keyGestureType = " + keyGestureTypeToString(mKeyGestureEvent.gestureType) + ", "
                 + "action = " + mKeyGestureEvent.action + ", "
                 + "displayId = " + mKeyGestureEvent.displayId + ", "
-                + "flags = " + mKeyGestureEvent.flags
+                + "flags = " + mKeyGestureEvent.flags + ", "
+                + "appLaunchData = " + getAppLaunchData()
                 + " }";
     }
 
@@ -339,7 +402,11 @@
                 && mKeyGestureEvent.gestureType == that.mKeyGestureEvent.gestureType
                 && mKeyGestureEvent.action == that.mKeyGestureEvent.action
                 && mKeyGestureEvent.displayId == that.mKeyGestureEvent.displayId
-                && mKeyGestureEvent.flags == that.mKeyGestureEvent.flags;
+                && mKeyGestureEvent.flags == that.mKeyGestureEvent.flags
+                && Objects.equals(mKeyGestureEvent.appLaunchCategory, that.mKeyGestureEvent.appLaunchCategory)
+                && Objects.equals(mKeyGestureEvent.appLaunchRole, that.mKeyGestureEvent.appLaunchRole)
+                && Objects.equals(mKeyGestureEvent.appLaunchPackageName, that.mKeyGestureEvent.appLaunchPackageName)
+                && Objects.equals(mKeyGestureEvent.appLaunchClassName, that.mKeyGestureEvent.appLaunchClassName);
     }
 
     @Override
@@ -352,13 +419,21 @@
         _hash = 31 * _hash + mKeyGestureEvent.action;
         _hash = 31 * _hash + mKeyGestureEvent.displayId;
         _hash = 31 * _hash + mKeyGestureEvent.flags;
+        _hash = 31 * _hash + (mKeyGestureEvent.appLaunchCategory != null
+                ? mKeyGestureEvent.appLaunchCategory.hashCode() : 0);
+        _hash = 31 * _hash + (mKeyGestureEvent.appLaunchRole != null
+                ? mKeyGestureEvent.appLaunchRole.hashCode() : 0);
+        _hash = 31 * _hash + (mKeyGestureEvent.appLaunchPackageName != null
+                ? mKeyGestureEvent.appLaunchPackageName.hashCode() : 0);
+        _hash = 31 * _hash + (mKeyGestureEvent.appLaunchClassName != null
+                ? mKeyGestureEvent.appLaunchClassName.hashCode() : 0);
         return _hash;
     }
 
     /**
      * Convert KeyGestureEvent type to corresponding log event got KeyboardSystemsEvent
      */
-    public static int keyGestureTypeToLogEvent(@KeyGestureType int value) {
+    private static int keyGestureTypeToLogEvent(@KeyGestureType int value) {
         switch (value) {
             case KEY_GESTURE_TYPE_HOME:
                 return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__HOME;
@@ -460,14 +535,79 @@
                 return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_WEATHER;
             case KEY_GESTURE_TYPE_LAUNCH_DEFAULT_FITNESS:
                 return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_FITNESS;
-            case KEY_GESTURE_TYPE_LAUNCH_APPLICATION_BY_PACKAGE_NAME:
+            case KEY_GESTURE_TYPE_LAUNCH_APPLICATION:
                 return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_APPLICATION_BY_PACKAGE_NAME;
             case KEY_GESTURE_TYPE_DESKTOP_MODE:
                 return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__DESKTOP_MODE;
             case KEY_GESTURE_TYPE_MULTI_WINDOW_NAVIGATION:
                 return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__MULTI_WINDOW_NAVIGATION;
             default:
-                return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__UNSPECIFIED;
+                return LOG_EVENT_UNSPECIFIED;
+        }
+    }
+
+    /**
+     * Find Log event type corresponding to app launch data.
+     * Returns {@code LOG_EVENT_UNSPECIFIED} if no matching event found
+     */
+    private static int getLogEventFromLaunchAppData(@Nullable AppLaunchData data) {
+        if (data == null) {
+            return LOG_EVENT_UNSPECIFIED;
+        }
+        if (data instanceof AppLaunchData.CategoryData) {
+            return getLogEventFromSelectorCategory(
+                    ((AppLaunchData.CategoryData) data).getCategory());
+        } else if (data instanceof AppLaunchData.RoleData) {
+            return getLogEventFromRole(((AppLaunchData.RoleData) data).getRole());
+        } else if (data instanceof AppLaunchData.ComponentData) {
+            return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_APPLICATION_BY_PACKAGE_NAME;
+        } else {
+            throw new IllegalArgumentException("AppLaunchData type is invalid!");
+        }
+    }
+
+    private static int getLogEventFromSelectorCategory(@NonNull String category) {
+        switch (category) {
+            case Intent.CATEGORY_APP_BROWSER:
+                return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_BROWSER;
+            case Intent.CATEGORY_APP_EMAIL:
+                return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_EMAIL;
+            case Intent.CATEGORY_APP_CONTACTS:
+                return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_CONTACTS;
+            case Intent.CATEGORY_APP_CALENDAR:
+                return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_CALENDAR;
+            case Intent.CATEGORY_APP_CALCULATOR:
+                return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_CALCULATOR;
+            case Intent.CATEGORY_APP_MUSIC:
+                return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_MUSIC;
+            case Intent.CATEGORY_APP_MAPS:
+                return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_MAPS;
+            case Intent.CATEGORY_APP_MESSAGING:
+                return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_MESSAGING;
+            case Intent.CATEGORY_APP_GALLERY:
+                return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_GALLERY;
+            case Intent.CATEGORY_APP_FILES:
+                return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_FILES;
+            case Intent.CATEGORY_APP_WEATHER:
+                return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_WEATHER;
+            case Intent.CATEGORY_APP_FITNESS:
+                return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_FITNESS;
+            default:
+                return LOG_EVENT_UNSPECIFIED;
+        }
+    }
+
+    /**
+     * Find Log event corresponding to the provide system role name.
+     * Returns {@code LOG_EVENT_UNSPECIFIED} if no matching event found.
+     */
+    private static int getLogEventFromRole(@NonNull String role) {
+        if (RoleManager.ROLE_BROWSER.equals(role)) {
+            return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_BROWSER;
+        } else if (RoleManager.ROLE_SMS.equals(role)) {
+            return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_MESSAGING;
+        } else {
+            return LOG_EVENT_UNSPECIFIED;
         }
     }
 
@@ -577,8 +717,8 @@
                 return "KEY_GESTURE_TYPE_LAUNCH_DEFAULT_WEATHER";
             case KEY_GESTURE_TYPE_LAUNCH_DEFAULT_FITNESS:
                 return "KEY_GESTURE_TYPE_LAUNCH_DEFAULT_FITNESS";
-            case KEY_GESTURE_TYPE_LAUNCH_APPLICATION_BY_PACKAGE_NAME:
-                return "KEY_GESTURE_TYPE_LAUNCH_APPLICATION_BY_PACKAGE_NAME";
+            case KEY_GESTURE_TYPE_LAUNCH_APPLICATION:
+                return "KEY_GESTURE_TYPE_LAUNCH_APPLICATION";
             case KEY_GESTURE_TYPE_DESKTOP_MODE:
                 return "KEY_GESTURE_TYPE_DESKTOP_MODE";
             case KEY_GESTURE_TYPE_MULTI_WINDOW_NAVIGATION:
@@ -601,6 +741,14 @@
                 return "KEY_GESTURE_TYPE_CLOSE_ALL_DIALOGS";
             case KEY_GESTURE_TYPE_TOGGLE_TALKBACK:
                 return "KEY_GESTURE_TYPE_TOGGLE_TALKBACK";
+            case KEY_GESTURE_TYPE_TOGGLE_STICKY_KEYS:
+                return "KEY_GESTURE_TYPE_TOGGLE_STICKY_KEYS";
+            case KEY_GESTURE_TYPE_TOGGLE_BOUNCE_KEYS:
+                return "KEY_GESTURE_TYPE_TOGGLE_BOUNCE_KEYS";
+            case KEY_GESTURE_TYPE_TOGGLE_SLOW_KEYS:
+                return "KEY_GESTURE_TYPE_TOGGLE_SLOW_KEYS";
+            case KEY_GESTURE_TYPE_TOGGLE_MOUSE_KEYS:
+                return "KEY_GESTURE_TYPE_TOGGLE_MOUSE_KEYS";
             default:
                 return Integer.toHexString(value);
         }
diff --git a/core/java/android/hardware/input/KeyGlyphMap.java b/core/java/android/hardware/input/KeyGlyphMap.java
index b517a63..f82d1cf 100644
--- a/core/java/android/hardware/input/KeyGlyphMap.java
+++ b/core/java/android/hardware/input/KeyGlyphMap.java
@@ -34,6 +34,7 @@
 import java.util.Arrays;
 import java.util.HashMap;
 import java.util.Map;
+import java.util.Objects;
 
 /**
  * This class provides access to device specific key glyphs, modifier glyphs and device specific
@@ -107,7 +108,54 @@
     /**
      * Defines a key combination that includes a keycode and modifier state.
      */
-    public record KeyCombination(int modifierState, int keycode) {}
+    public static class KeyCombination implements Parcelable {
+        private final int mModifierState;
+        private final int mKeycode;
+
+        public KeyCombination(int modifierState, int keycode) {
+            this.mModifierState = modifierState;
+            this.mKeycode = keycode;
+        }
+
+        public KeyCombination(Parcel in) {
+            this(in.readInt(), in.readInt());
+        }
+
+        public static final Creator<KeyCombination> CREATOR = new Creator<>() {
+            @Override
+            public KeyCombination createFromParcel(Parcel in) {
+                return new KeyCombination(in);
+            }
+
+            @Override
+            public KeyCombination[] newArray(int size) {
+                return new KeyCombination[size];
+            }
+        };
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public void writeToParcel(@androidx.annotation.NonNull Parcel dest, int flags) {
+            dest.writeInt(mModifierState);
+            dest.writeInt(mKeycode);
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (!(o instanceof KeyCombination that)) return false;
+            return mModifierState == that.mModifierState && mKeycode == that.mKeycode;
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(mModifierState, mKeycode);
+        }
+    }
 
     /**
      * Returns keycodes generated from the functional row defined for the keyboard.
@@ -158,6 +206,15 @@
         return getDrawable(context, mModifierGlyphs.get(modifier, 0));
     }
 
+    /**
+     * Provides the drawable resource for the glyph for a modifier state (e.g. META_META_ON).
+     * Returns null if not available.
+     */
+    @Nullable
+    public Drawable getDrawableForModifierState(Context context, int modifierState) {
+        return getDrawable(context, mModifierGlyphs.get(modifierState, 0));
+    }
+
     @Nullable
     private Drawable getDrawable(Context context, @DrawableRes int drawableRes) {
         PackageManager pm = context.getPackageManager();
diff --git a/core/java/android/hardware/input/input_framework.aconfig b/core/java/android/hardware/input/input_framework.aconfig
index 6669754..e6982b9 100644
--- a/core/java/android/hardware/input/input_framework.aconfig
+++ b/core/java/android/hardware/input/input_framework.aconfig
@@ -141,3 +141,17 @@
   description: "Adds shortcuts to toggle and control a11y features"
   bug: "373458181"
 }
+
+flag {
+    name: "enable_customizable_input_gestures"
+    namespace: "input"
+    description: "Enables keyboard shortcut customization support"
+    bug: "365064144"
+}
+
+flag {
+  name: "override_power_key_behavior_in_focused_window"
+  namespace: "input_native"
+  description: "Allows privileged focused windows to capture power key events."
+  bug: "357144512"
+}
diff --git a/core/java/android/hardware/soundtrigger/SoundTrigger.java b/core/java/android/hardware/soundtrigger/SoundTrigger.java
index a1e7567..c6fd0ee 100644
--- a/core/java/android/hardware/soundtrigger/SoundTrigger.java
+++ b/core/java/android/hardware/soundtrigger/SoundTrigger.java
@@ -1627,7 +1627,7 @@
             boolean allowMultipleTriggers = in.readBoolean();
             KeyphraseRecognitionExtra[] keyphrases =
                     in.createTypedArray(KeyphraseRecognitionExtra.CREATOR);
-            byte[] data = in.readBlob();
+            byte[] data = in.createByteArray();
             int audioCapabilities = in.readInt();
             return new RecognitionConfig(captureRequested, allowMultipleTriggers, keyphrases, data,
                     audioCapabilities);
@@ -1638,7 +1638,7 @@
             dest.writeBoolean(mCaptureRequested);
             dest.writeBoolean(mAllowMultipleTriggers);
             dest.writeTypedArray(mKeyphrases, flags);
-            dest.writeBlob(mData);
+            dest.writeByteArray(mData);
             dest.writeInt(mAudioCapabilities);
         }
 
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 49e2358..8c3f0ef 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -603,12 +603,6 @@
     @EnabledSince(targetSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
     public static final long DISALLOW_INPUT_METHOD_INTERFACE_OVERRIDE = 148086656L;
 
-    /**
-     * Enable the logic to allow hiding the IME caption bar ("fake" IME navigation bar).
-     * @hide
-     */
-    public static final boolean ENABLE_HIDE_IME_CAPTION_BAR = true;
-
     LayoutInflater mInflater;
     TypedArray mThemeAttrs;
     @UnsupportedAppUsage
diff --git a/core/java/android/inputmethodservice/NavigationBarController.java b/core/java/android/inputmethodservice/NavigationBarController.java
index 3ce67b0..b08454d 100644
--- a/core/java/android/inputmethodservice/NavigationBarController.java
+++ b/core/java/android/inputmethodservice/NavigationBarController.java
@@ -16,7 +16,6 @@
 
 package android.inputmethodservice;
 
-import static android.inputmethodservice.InputMethodService.ENABLE_HIDE_IME_CAPTION_BAR;
 import static android.view.WindowInsets.Type.captionBar;
 import static android.view.WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS;
 
@@ -260,15 +259,15 @@
             setIconTintInternal(calculateTargetDarkIntensity(mAppearance,
                     mDrawLegacyNavigationBarBackground));
 
-            if (ENABLE_HIDE_IME_CAPTION_BAR) {
-                mNavigationBarFrame.setOnApplyWindowInsetsListener((view, insets) -> {
-                    if (mNavigationBarFrame != null) {
-                        boolean visible = insets.isVisible(captionBar());
-                        mNavigationBarFrame.setVisibility(visible ? View.VISIBLE : View.GONE);
-                    }
-                    return view.onApplyWindowInsets(insets);
-                });
-            }
+            mNavigationBarFrame.setOnApplyWindowInsetsListener((view, insets) -> {
+                if (mNavigationBarFrame != null) {
+                    // The IME window receives IME-specific captionBar insets, representing the
+                    // IME navigation bar.
+                    boolean visible = insets.isVisible(captionBar());
+                    mNavigationBarFrame.setVisibility(visible ? View.VISIBLE : View.GONE);
+                }
+                return view.onApplyWindowInsets(insets);
+            });
         }
 
         private void uninstallNavigationBarFrameIfNecessary() {
@@ -279,9 +278,7 @@
             if (parent instanceof ViewGroup) {
                 ((ViewGroup) parent).removeView(mNavigationBarFrame);
             }
-            if (ENABLE_HIDE_IME_CAPTION_BAR) {
-                mNavigationBarFrame.setOnApplyWindowInsetsListener(null);
-            }
+            mNavigationBarFrame.setOnApplyWindowInsetsListener(null);
             mNavigationBarFrame = null;
         }
 
@@ -474,9 +471,6 @@
                         decor.bringChildToFront(mNavigationBarFrame);
                     }
                 }
-                if (!ENABLE_HIDE_IME_CAPTION_BAR) {
-                    mNavigationBarFrame.setVisibility(View.VISIBLE);
-                }
             }
         }
 
@@ -497,10 +491,8 @@
                     mShouldShowImeSwitcherWhenImeIsShown;
             mShouldShowImeSwitcherWhenImeIsShown = shouldShowImeSwitcherWhenImeIsShown;
 
-            if (ENABLE_HIDE_IME_CAPTION_BAR) {
-                mService.mWindow.getWindow().getDecorView().getWindowInsetsController()
-                        .setImeCaptionBarInsetsHeight(getImeCaptionBarHeight());
-            }
+            mService.mWindow.getWindow().getDecorView().getWindowInsetsController()
+                    .setImeCaptionBarInsetsHeight(getImeCaptionBarHeight(imeDrawsImeNavBar));
 
             if (imeDrawsImeNavBar) {
                 installNavigationBarFrameIfNecessary();
@@ -608,9 +600,11 @@
 
         /**
          * Returns the height of the IME caption bar if this should be shown, or {@code 0} instead.
+         *
+         * @param imeDrawsImeNavBar whether the IME should show the IME navigation bar.
          */
-        private int getImeCaptionBarHeight() {
-            return mImeDrawsImeNavBar
+        private int getImeCaptionBarHeight(boolean imeDrawsImeNavBar) {
+            return imeDrawsImeNavBar
                     ? mService.getResources().getDimensionPixelSize(
                             com.android.internal.R.dimen.navigation_bar_frame_height)
                     : 0;
diff --git a/core/java/android/net/vcn/OWNERS b/core/java/android/net/vcn/OWNERS
index 2441e77..937699a 100644
--- a/core/java/android/net/vcn/OWNERS
+++ b/core/java/android/net/vcn/OWNERS
@@ -1,7 +1,6 @@
 set noparent
 
-benedictwong@google.com
-ckesting@google.com
 evitayan@google.com
-junyin@google.com
 nharold@google.com
+benedictwong@google.com #{LAST_RESORT_SUGGESTION}
+yangji@google.com #{LAST_RESORT_SUGGESTION}
diff --git a/core/java/android/os/BatteryUsageStatsQuery.java b/core/java/android/os/BatteryUsageStatsQuery.java
index e68c4ca..6325b00 100644
--- a/core/java/android/os/BatteryUsageStatsQuery.java
+++ b/core/java/android/os/BatteryUsageStatsQuery.java
@@ -370,7 +370,10 @@
          * and most battery stats resets.
          */
         public Builder accumulated() {
-            mFlags |= FLAG_BATTERY_USAGE_STATS_ACCUMULATED;
+            mFlags |= FLAG_BATTERY_USAGE_STATS_ACCUMULATED
+                    | FLAG_BATTERY_USAGE_STATS_INCLUDE_POWER_STATE
+                    | FLAG_BATTERY_USAGE_STATS_INCLUDE_SCREEN_STATE
+                    | FLAG_BATTERY_USAGE_STATS_INCLUDE_PROCESS_STATE_DATA;
             return this;
         }
 
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index a894833..13d7e3c 100644
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -1274,6 +1274,12 @@
          * Vanilla Ice Cream.
          */
         public static final int VANILLA_ICE_CREAM = 35;
+
+        /**
+         * Baklava.
+         */
+        @FlaggedApi(Flags.FLAG_MAJOR_MINOR_VERSIONING_SCHEME)
+        public static final int BAKLAVA = CUR_DEVELOPMENT;
     }
 
     /** @hide */
@@ -1313,6 +1319,7 @@
         VERSION_CODES_FULL.TIRAMISU,
         VERSION_CODES_FULL.UPSIDE_DOWN_CAKE,
         VERSION_CODES_FULL.VANILLA_ICE_CREAM,
+        VERSION_CODES_FULL.BAKLAVA,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface SdkIntFull {}
@@ -1514,6 +1521,11 @@
          */
         public static final int VANILLA_ICE_CREAM =
                 VERSION_CODES.VANILLA_ICE_CREAM * SDK_INT_MULTIPLIER;
+
+        /**
+         * The upcoming, not yet finalized, version of Android.
+         */
+        public static final int BAKLAVA = VERSION_CODES.BAKLAVA * SDK_INT_MULTIPLIER;
     }
 
     /**
diff --git a/core/java/android/os/CombinedVibration.java b/core/java/android/os/CombinedVibration.java
index f1d3957..8fbba4f 100644
--- a/core/java/android/os/CombinedVibration.java
+++ b/core/java/android/os/CombinedVibration.java
@@ -194,7 +194,6 @@
         int[] getAvailableVibratorIds();
 
         /** Adapts a {@link VibrationEffect} to a given vibrator. */
-        @NonNull
         VibrationEffect adaptToVibrator(int vibratorId, @NonNull VibrationEffect effect);
     }
 
@@ -442,6 +441,10 @@
             boolean hasSameEffects = true;
             for (int vibratorId : adapter.getAvailableVibratorIds()) {
                 VibrationEffect newEffect = adapter.adaptToVibrator(vibratorId, mEffect);
+                if (newEffect == null) {
+                    // The vibration effect contains unsupported segments and cannot be played.
+                    return null;
+                }
                 combination.addVibrator(vibratorId, newEffect);
                 hasSameEffects &= mEffect.equals(newEffect);
             }
@@ -649,6 +652,10 @@
                 int vibratorId = mEffects.keyAt(i);
                 VibrationEffect effect = mEffects.valueAt(i);
                 VibrationEffect newEffect = adapter.adaptToVibrator(vibratorId, effect);
+                if (newEffect == null) {
+                    // The vibration effect contains unsupported segments and cannot be played.
+                    return null;
+                }
                 combination.addVibrator(vibratorId, newEffect);
                 hasSameEffects &= effect.equals(newEffect);
             }
diff --git a/core/java/android/os/IpcDataCache.java b/core/java/android/os/IpcDataCache.java
index e2a72dd..7875c23 100644
--- a/core/java/android/os/IpcDataCache.java
+++ b/core/java/android/os/IpcDataCache.java
@@ -257,6 +257,7 @@
  */
 @TestApi
 @SystemApi(client=SystemApi.Client.MODULE_LIBRARIES)
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
 public class IpcDataCache<Query, Result> extends PropertyInvalidatedCache<Query, Result> {
     /**
      * {@inheritDoc}
diff --git a/core/java/android/os/RemoteCallbackList.java b/core/java/android/os/RemoteCallbackList.java
index f82c822..01b1e5e1 100644
--- a/core/java/android/os/RemoteCallbackList.java
+++ b/core/java/android/os/RemoteCallbackList.java
@@ -19,6 +19,7 @@
 import android.annotation.FlaggedApi;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.util.ArrayMap;
 import android.util.Slog;
@@ -223,6 +224,7 @@
     public static final class Builder<E extends IInterface> {
         private @FrozenCalleePolicy int mFrozenCalleePolicy;
         private int mMaxQueueSize = DEFAULT_MAX_QUEUE_SIZE;
+        private InterfaceDiedCallback mInterfaceDiedCallback;
 
         /**
          * Creates a Builder for {@link RemoteCallbackList}.
@@ -262,11 +264,46 @@
         }
 
         /**
+         * Sets the callback to be invoked when an interface dies.
+         */
+        public @NonNull Builder setInterfaceDiedCallback(
+                @NonNull InterfaceDiedCallback<E> callback) {
+            mInterfaceDiedCallback = callback;
+            return this;
+        }
+
+        /**
+         * For notifying when the process hosting a callback interface has died.
+         *
+         * @param <E> The remote callback interface type.
+         */
+        @FlaggedApi(Flags.FLAG_BINDER_FROZEN_STATE_CHANGE_CALLBACK)
+        public interface InterfaceDiedCallback<E extends IInterface> {
+            /**
+             * Invoked when a callback interface has died.
+             *
+             * @param remoteCallbackList the list that the interface was registered with.
+             * @param deadInterface the interface that has died.
+             * @param cookie the cookie specified on interface registration.
+             */
+            void onInterfaceDied(@NonNull RemoteCallbackList<E> remoteCallbackList,
+                    E deadInterface, @Nullable Object cookie);
+        }
+
+        /**
          * Builds and returns a {@link RemoteCallbackList}.
          *
          * @return The built {@link RemoteCallbackList} object.
          */
         public @NonNull RemoteCallbackList<E> build() {
+            if (mInterfaceDiedCallback != null) {
+                return new RemoteCallbackList<E>(mFrozenCalleePolicy, mMaxQueueSize) {
+                    @Override
+                    public void onCallbackDied(E deadInterface, Object cookie) {
+                        mInterfaceDiedCallback.onInterfaceDied(this, deadInterface, cookie);
+                    }
+                };
+            }
             return new RemoteCallbackList<E>(mFrozenCalleePolicy, mMaxQueueSize);
         }
     }
diff --git a/core/java/android/os/StrictMode.java b/core/java/android/os/StrictMode.java
index 81dc46e..60a9e05 100644
--- a/core/java/android/os/StrictMode.java
+++ b/core/java/android/os/StrictMode.java
@@ -20,17 +20,22 @@
 import static com.android.internal.util.FrameworkStatsLog.UNSAFE_INTENT_EVENT_REPORTED__EVENT_TYPE__EXPLICIT_INTENT_FILTER_UNMATCH;
 import static com.android.internal.util.FrameworkStatsLog.UNSAFE_INTENT_EVENT_REPORTED__EVENT_TYPE__INTERNAL_NON_EXPORTED_COMPONENT_MATCH;
 import static com.android.internal.util.FrameworkStatsLog.UNSAFE_INTENT_EVENT_REPORTED__EVENT_TYPE__NULL_ACTION_MATCH;
+import static com.android.window.flags.Flags.balStrictMode;
 
 import android.animation.ValueAnimator;
+import android.annotation.FlaggedApi;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
 import android.app.ActivityManager;
+import android.app.ActivityTaskManager;
 import android.app.ActivityThread;
 import android.app.IActivityManager;
+import android.app.IBackgroundActivityLaunchCallback;
 import android.app.IUnsafeIntentStrictModeCallback;
+import android.app.PendingIntent;
 import android.app.compat.CompatChanges;
 import android.compat.annotation.ChangeId;
 import android.compat.annotation.EnabledSince;
@@ -45,6 +50,7 @@
 import android.net.TrafficStats;
 import android.net.Uri;
 import android.os.storage.IStorageManager;
+import android.os.strictmode.BackgroundActivityLaunchViolation;
 import android.os.strictmode.CleartextNetworkViolation;
 import android.os.strictmode.ContentUriWithoutPermissionViolation;
 import android.os.strictmode.CredentialProtectedWhileLockedViolation;
@@ -82,6 +88,7 @@
 import com.android.internal.util.FastPrintWriter;
 import com.android.internal.util.HexDump;
 import com.android.internal.util.Preconditions;
+import com.android.window.flags.Flags;
 
 import dalvik.system.BlockGuard;
 import dalvik.system.CloseGuard;
@@ -266,6 +273,7 @@
             DETECT_VM_IMPLICIT_DIRECT_BOOT,
             DETECT_VM_INCORRECT_CONTEXT_USE,
             DETECT_VM_UNSAFE_INTENT_LAUNCH,
+            DETECT_VM_BACKGROUND_ACTIVITY_LAUNCH_ABORTED,
             PENALTY_GATHER,
             PENALTY_LOG,
             PENALTY_DIALOG,
@@ -309,6 +317,8 @@
     private static final int DETECT_VM_INCORRECT_CONTEXT_USE = 1 << 12;
     /** @hide */
     private static final int DETECT_VM_UNSAFE_INTENT_LAUNCH = 1 << 13;
+    /** @hide */
+    private static final int DETECT_VM_BACKGROUND_ACTIVITY_LAUNCH_ABORTED = 1 << 14;
 
     /** @hide */
     private static final int DETECT_VM_ALL = 0x0000ffff;
@@ -902,6 +912,9 @@
                 if (targetSdk >= Build.VERSION_CODES.S) {
                     detectUnsafeIntentLaunch();
                 }
+                if (balStrictMode() && targetSdk > Build.VERSION_CODES.VANILLA_ICE_CREAM) {
+                    detectBlockedBackgroundActivityLaunch();
+                }
 
                 // TODO: Decide whether to detect non SDK API usage beyond a certain API level.
                 // TODO: enable detectImplicitDirectBoot() once system is less noisy
@@ -1140,6 +1153,39 @@
             }
 
             /**
+             * Detects when your app is blocked from launching a background activity or a
+             * PendingIntent created by your app cannot be launched.
+             * <p>
+             * Starting an activity requires <a
+             * href="https://developer.android.com/guide/components/activities/background-starts
+             * ">specific permissions</a> which may depend on the state at runtime and especially
+             * in case of {@link android.app.PendingIntent} starts on the collaborating app.
+             * If the activity start is blocked methods like {@link Context#startActivity(Intent)}
+             * or {@link PendingIntent#send()} have no way to return that information. Instead you
+             * can use this strct mode feature to detect blocked starts.
+             * <p>
+             * Note that in some cases blocked starts may be unavoidable, e.g. when the user clicks
+             * the home button while the app tries to start a new activity.
+             */
+            @SuppressWarnings("BuilderSetStyle")
+            @FlaggedApi(Flags.FLAG_BAL_STRICT_MODE)
+            public @NonNull Builder detectBlockedBackgroundActivityLaunch() {
+                return enable(DETECT_VM_BACKGROUND_ACTIVITY_LAUNCH_ABORTED);
+            }
+
+            /**
+             * Stops detecting whether your app is blocked from launching a background activity or
+             * a PendingIntent created by your app cannot be launched.
+             * <p>
+             * This disables the effect of {@link #detectBlockedBackgroundActivityLaunch()}.
+             */
+            @SuppressWarnings("BuilderSetStyle")
+            @FlaggedApi(Flags.FLAG_BAL_STRICT_MODE)
+            public @NonNull Builder ignoreBlockedBackgroundActivityLaunch() {
+                return disable(DETECT_VM_BACKGROUND_ACTIVITY_LAUNCH_ABORTED);
+            }
+
+            /**
              * Crashes the whole process on violation. This penalty runs at the end of all enabled
              * penalties so you'll still get your logging or other violations before the process
              * dies.
@@ -2133,10 +2179,25 @@
                 registerIntentMatchingRestrictionCallback();
             }
 
+            if ((sVmPolicy.mask & DETECT_VM_BACKGROUND_ACTIVITY_LAUNCH_ABORTED) != 0) {
+                registerBackgroundActivityLaunchCallback();
+            }
+
             setBlockGuardVmPolicy(sVmPolicy.mask);
         }
     }
 
+    private static void registerBackgroundActivityLaunchCallback() {
+        try {
+            ActivityTaskManager.getService().registerBackgroundActivityStartCallback(
+                    new BackgroundActivityLaunchCallback());
+        } catch (DeadObjectException e) {
+            // ignore
+        } catch (RemoteException e) {
+            Log.e(TAG, "RemoteException handling StrictMode violation", e);
+        }
+    }
+
     private static final class UnsafeIntentStrictModeCallback
             extends IUnsafeIntentStrictModeCallback.Stub {
         @Override
@@ -2161,6 +2222,16 @@
         }
     }
 
+    private static final class BackgroundActivityLaunchCallback
+            extends IBackgroundActivityLaunchCallback.Stub {
+        @Override
+        public void onBackgroundActivityLaunchAborted(String message) {
+            if (StrictMode.vmBackgroundActivityLaunchEnabled()) {
+                StrictMode.onBackgroundActivityLaunchAborted(message);
+            }
+        }
+    }
+
     /** Gets the current VM policy. */
     public static VmPolicy getVmPolicy() {
         synchronized (StrictMode.class) {
@@ -2236,6 +2307,11 @@
     }
 
     /** @hide */
+    public static boolean vmBackgroundActivityLaunchEnabled() {
+        return (sVmPolicy.mask & DETECT_VM_BACKGROUND_ACTIVITY_LAUNCH_ABORTED) != 0;
+    }
+
+    /** @hide */
     public static void onSqliteObjectLeaked(String message, Throwable originStack) {
         onVmPolicyViolation(new SqliteObjectLeakedViolation(message, originStack));
     }
@@ -2402,6 +2478,11 @@
         onVmPolicyViolation(new UnsafeIntentLaunchViolation(intent, msg + intent));
     }
 
+    /** @hide */
+    public static void onBackgroundActivityLaunchAborted(String message) {
+        onVmPolicyViolation(new BackgroundActivityLaunchViolation(message));
+    }
+
     /** Assume locked until we hear otherwise */
     private static volatile boolean sCeStorageUnlocked = false;
 
diff --git a/core/java/android/os/VibrationEffect.java b/core/java/android/os/VibrationEffect.java
index 61dd11f..a3844e2 100644
--- a/core/java/android/os/VibrationEffect.java
+++ b/core/java/android/os/VibrationEffect.java
@@ -37,6 +37,7 @@
 import android.os.vibrator.Flags;
 import android.os.vibrator.PrebakedSegment;
 import android.os.vibrator.PrimitiveSegment;
+import android.os.vibrator.PwleSegment;
 import android.os.vibrator.RampSegment;
 import android.os.vibrator.StepSegment;
 import android.os.vibrator.VibrationEffectSegment;
@@ -1403,6 +1404,49 @@
     }
 
     /**
+     * Creates a new {@link VibrationEffect} that repeats the given effect indefinitely.
+     *
+     * <p>The input vibration must not be a repeating vibration. If it is, an
+     * {@link IllegalArgumentException} will be thrown.
+     *
+     * @param effect The {@link VibrationEffect} that will be repeated.
+     * @return A {@link VibrationEffect} that repeats the effect indefinitely.
+     * @throws IllegalArgumentException if the effect is already a repeating vibration.
+     */
+    @FlaggedApi(Flags.FLAG_NORMALIZED_PWLE_EFFECTS)
+    @NonNull
+    public static VibrationEffect createRepeatingEffect(@NonNull VibrationEffect effect) {
+        return VibrationEffect.startComposition()
+                .repeatEffectIndefinitely(effect)
+                .compose();
+    }
+
+    /**
+     * Creates a new {@link VibrationEffect} by merging the preamble and repeating vibration effect.
+     *
+     * <p>Neither input vibration may already be repeating. An {@link IllegalArgumentException} will
+     * be thrown if either input vibration is set to repeat indefinitely.
+     *
+     * @param preamble        The starting vibration effect, which must be finite.
+     * @param repeatingEffect The vibration effect to be repeated indefinitely after the preamble.
+     * @return A {@link VibrationEffect} that plays the preamble once followed by the
+     * `repeatingEffect` indefinitely.
+     * @throws IllegalArgumentException if either preamble or repeatingEffect is already a repeating
+     *                                  vibration.
+     */
+    @FlaggedApi(Flags.FLAG_NORMALIZED_PWLE_EFFECTS)
+    @NonNull
+    public static VibrationEffect createRepeatingEffect(@NonNull VibrationEffect preamble,
+            @NonNull VibrationEffect repeatingEffect) {
+        Preconditions.checkArgument(preamble.getDuration() < Long.MAX_VALUE,
+                "Can't repeat an indefinitely repeating effect.");
+        return VibrationEffect.startComposition()
+                .addEffect(preamble)
+                .repeatEffectIndefinitely(repeatingEffect)
+                .compose();
+    }
+
+    /**
      * A composition of haptic elements that are combined to be playable as a single
      * {@link VibrationEffect}.
      *
@@ -1692,6 +1736,147 @@
     }
 
     /**
+     * Start building a waveform vibration.
+     *
+     * <p>The waveform envelope builder offers more flexibility for creating waveform effects,
+     * allowing control over vibration amplitude and frequency via smooth transitions between
+     * values.
+     *
+     * <p>Note: To check whether waveform envelope effects are supported, use
+     * {@link Vibrator#areEnvelopeEffectsSupported()}.
+     *
+     * @see VibrationEffect.WaveformEnvelopeBuilder
+     */
+    @FlaggedApi(Flags.FLAG_NORMALIZED_PWLE_EFFECTS)
+    @NonNull
+    public static VibrationEffect.WaveformEnvelopeBuilder startWaveformEnvelope() {
+        return new WaveformEnvelopeBuilder();
+    }
+
+    /**
+     * A builder for waveform effects described by its envelope.
+     *
+     * <p>Waveform effect envelopes are defined by one or more control points describing a target
+     * vibration amplitude and frequency, and a duration to reach those targets. The vibrator
+     * will perform smooth transitions between control points.
+     *
+     * <p>For example, the following code ramps a vibrator from off to full amplitude at 120Hz over
+     * 100ms, holds that state for 200ms, and then ramps back down over 100ms:
+     *
+     * <pre>{@code
+     * VibrationEffect effect = VibrationEffect.startWaveformEnvelope()
+     *     .addControlPoint(1.0f, 120f, 100)
+     *     .addControlPoint(1.0f, 120f, 200)
+     *     .addControlPoint(0.0f, 120f, 100)
+     *     .build();
+     * }</pre>
+     *
+     * <p>It is crucial to ensure that the frequency range used in your effect is compatible with
+     * the device's capabilities. The framework will not play any frequencies that fall partially
+     * or completely outside the device's supported range. It will also not attempt to correct or
+     * modify these frequencies.
+     *
+     * <p>Therefore, it is strongly recommended that you design your haptic effects with the
+     * device's frequency profile in mind. You can obtain the supported frequency range and other
+     * relevant frequency-related information by getting the
+     * {@link android.os.vibrator.VibratorFrequencyProfile} using the
+     * {@link Vibrator#getFrequencyProfile()} method.
+     *
+     * <p>In addition to these limitations, when designing vibration patterns, it is important to
+     * consider the physical limitations of the vibration actuator. These limitations include
+     * factors such as the maximum number of control points allowed in an envelope effect, the
+     * minimum and maximum durations permitted for each control point, and the maximum overall
+     * duration of the effect. If a pattern exceeds the maximum number of allowed control points,
+     * the framework will automatically break down the effect to ensure it plays correctly.
+     *
+     * <p>You can use the following APIs to obtain these limits:
+     * <ul>
+     * <li>Maximum envelope control points: {@link Vibrator#getMaxEnvelopeEffectSize()}</li>
+     * <li>Minimum control point duration:
+     * {@link Vibrator#getMinEnvelopeEffectControlPointDurationMillis()}</li>
+     * <li>Maximum control point duration:
+     * {@link Vibrator#getMaxEnvelopeEffectControlPointDurationMillis()}</li>
+     * <li>Maximum total effect duration: {@link Vibrator#getMaxEnvelopeEffectDurationMillis()}</li>
+     * </ul>
+     *
+     * @see VibrationEffect#startWaveformEnvelope()
+     */
+    @FlaggedApi(Flags.FLAG_NORMALIZED_PWLE_EFFECTS)
+    public static final class WaveformEnvelopeBuilder {
+
+        private ArrayList<PwleSegment> mSegments = new ArrayList<>();
+        private float mLastAmplitude = 0f;
+        private float mLastFrequencyHz = 0f;
+
+        private WaveformEnvelopeBuilder() {}
+
+        /**
+         * Adds a new control point to the end of this waveform envelope.
+         *
+         * <p>Amplitude defines the vibrator's strength at this frequency, ranging from 0 (off) to 1
+         * (maximum achievable strength). This value scales linearly with output strength, not
+         * perceived intensity. It's determined by the actuator response curve.
+         *
+         * <p>Frequency must be greater than zero and within the supported range. To determine
+         * the supported range, use {@link Vibrator#getFrequencyProfile()}. This method returns a
+         * {@link android.os.vibrator.VibratorFrequencyProfile} object, which contains the
+         * minimum and maximum frequencies, among other frequency-related information. Creating
+         * effects using frequencies outside this range will result in the vibration not playing.
+         *
+         * <p>Time specifies the duration (in milliseconds) for the vibrator to smoothly transition
+         * from the previous control point to this new one. It must be greater than zero. To
+         * transition as quickly as possible, use
+         * {@link Vibrator#getMinEnvelopeEffectControlPointDurationMillis()}.
+         *
+         * @param amplitude   The amplitude value between 0 and 1, inclusive. 0 represents the
+         *                    vibrator being off, and 1 represents the maximum achievable amplitude
+         *                    at this frequency.
+         * @param frequencyHz The frequency in Hz, must be greater than zero.
+         * @param timeMillis  The transition time in milliseconds.
+         */
+        @FlaggedApi(Flags.FLAG_NORMALIZED_PWLE_EFFECTS)
+        @SuppressWarnings("MissingGetterMatchingBuilder") // No getters to segments once created.
+        @NonNull
+        public WaveformEnvelopeBuilder addControlPoint(
+                @FloatRange(from = 0, to = 1) float amplitude,
+                @FloatRange(from = 0) float frequencyHz, int timeMillis) {
+
+            if (mSegments.isEmpty()) {
+                mLastFrequencyHz = frequencyHz;
+            }
+
+            mSegments.add(new PwleSegment(mLastAmplitude, amplitude, mLastFrequencyHz, frequencyHz,
+                    timeMillis));
+
+            mLastAmplitude = amplitude;
+            mLastFrequencyHz = frequencyHz;
+
+            return this;
+        }
+
+        /**
+         * Build the waveform as a single {@link VibrationEffect}.
+         *
+         * <p>The {@link WaveformEnvelopeBuilder} object is still valid after this call, so you can
+         * continue adding more primitives to it and generating more {@link VibrationEffect}s by
+         * calling this method again.
+         *
+         * @return The {@link VibrationEffect} resulting from the list of control points.
+         */
+        @FlaggedApi(Flags.FLAG_NORMALIZED_PWLE_EFFECTS)
+        @NonNull
+        public VibrationEffect build() {
+            if (mSegments.isEmpty()) {
+                throw new IllegalStateException(
+                        "WaveformEnvelopeBuilder must have at least one control point to build.");
+            }
+            VibrationEffect effect = new Composed(mSegments, /* repeatIndex= */ -1);
+            effect.validate();
+            return effect;
+        }
+    }
+
+    /**
      * A builder for waveform haptic effects.
      *
      * <p>Waveform vibrations constitute of one or more timed transitions to new sets of vibration
diff --git a/core/java/android/os/strictmode/BackgroundActivityLaunchViolation.java b/core/java/android/os/strictmode/BackgroundActivityLaunchViolation.java
new file mode 100644
index 0000000..aef52c6
--- /dev/null
+++ b/core/java/android/os/strictmode/BackgroundActivityLaunchViolation.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os.strictmode;
+
+import android.annotation.NonNull;
+import android.app.Activity;
+
+/**
+ * Violation raised when your app is blocked from launching an {@link Activity}
+ * (from the background).
+ * <p>
+ * This occurs when the app:
+ * <ul>
+ *     <li>Does not have sufficient privileges to launch the Activity.</li>
+ *     <li>Has not explicitly opted-in to launch the Activity.</li>
+ * </ul>
+ * Violations may affect the functionality of your app and should be addressed to ensure
+ * proper behavior.
+ * @hide
+ */
+public class BackgroundActivityLaunchViolation extends Violation {
+
+    /** @hide */
+    public BackgroundActivityLaunchViolation(@NonNull String message) {
+        super(message);
+    }
+}
diff --git a/core/java/android/os/vibrator/PwleSegment.java b/core/java/android/os/vibrator/PwleSegment.java
new file mode 100644
index 0000000..9074bde
--- /dev/null
+++ b/core/java/android/os/vibrator/PwleSegment.java
@@ -0,0 +1,234 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os.vibrator;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.TestApi;
+import android.os.Parcel;
+import android.os.VibrationEffect;
+import android.os.VibratorInfo;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.Locale;
+import java.util.Objects;
+
+/**
+ * A {@link VibrationEffectSegment} that represents a smooth transition from the starting
+ * amplitude and frequency to new values over a specified duration.
+ *
+ * <p>The amplitudes are expressed by float values in the range [0, 1], representing the relative
+ * output acceleration for the vibrator. The frequencies are expressed in hertz by positive finite
+ * float values.
+ * @hide
+ */
+@TestApi
+@FlaggedApi(Flags.FLAG_NORMALIZED_PWLE_EFFECTS)
+public final class PwleSegment extends VibrationEffectSegment {
+    private final float mStartAmplitude;
+    private final float mStartFrequencyHz;
+    private final float mEndAmplitude;
+    private final float mEndFrequencyHz;
+    private final int mDuration;
+
+    PwleSegment(@android.annotation.NonNull Parcel in) {
+        this(in.readFloat(), in.readFloat(), in.readFloat(), in.readFloat(), in.readInt());
+    }
+
+    /** @hide */
+    @FlaggedApi(Flags.FLAG_NORMALIZED_PWLE_EFFECTS)
+    public PwleSegment(float startAmplitude, float endAmplitude, float startFrequencyHz,
+            float endFrequencyHz, int duration) {
+        mStartAmplitude = startAmplitude;
+        mEndAmplitude = endAmplitude;
+        mStartFrequencyHz = startFrequencyHz;
+        mEndFrequencyHz = endFrequencyHz;
+        mDuration = duration;
+    }
+
+    public float getStartAmplitude() {
+        return mStartAmplitude;
+    }
+
+    public float getEndAmplitude() {
+        return mEndAmplitude;
+    }
+
+    public float getStartFrequencyHz() {
+        return mStartFrequencyHz;
+    }
+
+    public float getEndFrequencyHz() {
+        return mEndFrequencyHz;
+    }
+
+    @Override
+    public long getDuration() {
+        return mDuration;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (!(o instanceof PwleSegment)) {
+            return false;
+        }
+        PwleSegment other = (PwleSegment) o;
+        return Float.compare(mStartAmplitude, other.mStartAmplitude) == 0
+                && Float.compare(mEndAmplitude, other.mEndAmplitude) == 0
+                && Float.compare(mStartFrequencyHz, other.mStartFrequencyHz) == 0
+                && Float.compare(mEndFrequencyHz, other.mEndFrequencyHz) == 0
+                && mDuration == other.mDuration;
+    }
+
+    /** @hide */
+    @Override
+    public boolean areVibrationFeaturesSupported(@NonNull VibratorInfo vibratorInfo) {
+        boolean areFeaturesSupported = vibratorInfo.areEnvelopeEffectsSupported();
+
+        // Check that the frequency is within the supported range
+        float minFrequency = vibratorInfo.getFrequencyProfile().getMinFrequencyHz();
+        float maxFrequency = vibratorInfo.getFrequencyProfile().getMaxFrequencyHz();
+
+        areFeaturesSupported &=
+                mStartFrequencyHz >= minFrequency && mStartFrequencyHz <= maxFrequency
+                        && mEndFrequencyHz >= minFrequency && mEndFrequencyHz <= maxFrequency;
+
+        return areFeaturesSupported;
+    }
+
+    /** @hide */
+    @Override
+    public boolean isHapticFeedbackCandidate() {
+        return true;
+    }
+
+    /** @hide */
+    @Override
+    public void validate() {
+        Preconditions.checkArgumentPositive(mStartFrequencyHz,
+                "Start frequency must be greater than zero.");
+        Preconditions.checkArgumentPositive(mEndFrequencyHz,
+                "End frequency must be greater than zero.");
+        Preconditions.checkArgumentPositive(mDuration, "Time must be greater than zero.");
+
+        Preconditions.checkArgumentInRange(mStartAmplitude, 0f, 1f, "startAmplitude");
+        Preconditions.checkArgumentInRange(mEndAmplitude, 0f, 1f, "endAmplitude");
+    }
+
+    /** @hide */
+    @NonNull
+    @Override
+    public PwleSegment resolve(int defaultAmplitude) {
+        return this;
+    }
+
+    /** @hide */
+    @NonNull
+    @Override
+    public PwleSegment scale(float scaleFactor) {
+        float newStartAmplitude = VibrationEffect.scale(mStartAmplitude, scaleFactor);
+        float newEndAmplitude = VibrationEffect.scale(mEndAmplitude, scaleFactor);
+        if (Float.compare(mStartAmplitude, newStartAmplitude) == 0
+                && Float.compare(mEndAmplitude, newEndAmplitude) == 0) {
+            return this;
+        }
+        return new PwleSegment(newStartAmplitude, newEndAmplitude, mStartFrequencyHz,
+                mEndFrequencyHz,
+                mDuration);
+    }
+
+    /** @hide */
+    @NonNull
+    @Override
+    public PwleSegment scaleLinearly(float scaleFactor) {
+        float newStartAmplitude = VibrationEffect.scaleLinearly(mStartAmplitude, scaleFactor);
+        float newEndAmplitude = VibrationEffect.scaleLinearly(mEndAmplitude, scaleFactor);
+        if (Float.compare(mStartAmplitude, newStartAmplitude) == 0
+                && Float.compare(mEndAmplitude, newEndAmplitude) == 0) {
+            return this;
+        }
+        return new PwleSegment(newStartAmplitude, newEndAmplitude, mStartFrequencyHz,
+                mEndFrequencyHz,
+                mDuration);
+    }
+
+    /** @hide */
+    @NonNull
+    @Override
+    public PwleSegment applyEffectStrength(int effectStrength) {
+        return this;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mStartAmplitude, mEndAmplitude, mStartFrequencyHz, mEndFrequencyHz,
+                mDuration);
+    }
+
+    @Override
+    public String toString() {
+        return "Pwle{startAmplitude=" + mStartAmplitude
+                + ", endAmplitude=" + mEndAmplitude
+                + ", startFrequencyHz=" + mStartFrequencyHz
+                + ", endFrequencyHz=" + mEndFrequencyHz
+                + ", duration=" + mDuration
+                + "}";
+    }
+
+    /** @hide */
+    @Override
+    public String toDebugString() {
+        return String.format(Locale.US, "Pwle=%dms(amplitude=%.2f @ %.2fHz to %.2f @ %.2fHz)",
+                mDuration,
+                mStartAmplitude,
+                mStartFrequencyHz,
+                mEndAmplitude,
+                mEndFrequencyHz);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeInt(PARCEL_TOKEN_PWLE);
+        dest.writeFloat(mStartAmplitude);
+        dest.writeFloat(mEndAmplitude);
+        dest.writeFloat(mStartFrequencyHz);
+        dest.writeFloat(mEndFrequencyHz);
+        dest.writeInt(mDuration);
+    }
+
+    @android.annotation.NonNull
+    public static final Creator<PwleSegment> CREATOR =
+            new Creator<PwleSegment>() {
+                @Override
+                public PwleSegment createFromParcel(Parcel in) {
+                    // Skip the type token
+                    in.readInt();
+                    return new PwleSegment(in);
+                }
+
+                @Override
+                public PwleSegment[] newArray(int size) {
+                    return new PwleSegment[size];
+                }
+            };
+}
diff --git a/core/java/android/os/vibrator/VibrationEffectSegment.java b/core/java/android/os/vibrator/VibrationEffectSegment.java
index dadc849..b934e11 100644
--- a/core/java/android/os/vibrator/VibrationEffectSegment.java
+++ b/core/java/android/os/vibrator/VibrationEffectSegment.java
@@ -46,6 +46,7 @@
     static final int PARCEL_TOKEN_PRIMITIVE = 2;
     static final int PARCEL_TOKEN_STEP = 3;
     static final int PARCEL_TOKEN_RAMP = 4;
+    static final int PARCEL_TOKEN_PWLE = 5;
 
     /** Prevent subclassing from outside of this package */
     VibrationEffectSegment() {
@@ -223,6 +224,11 @@
                             return new PrebakedSegment(in);
                         case PARCEL_TOKEN_PRIMITIVE:
                             return new PrimitiveSegment(in);
+                        case PARCEL_TOKEN_PWLE:
+                            if (Flags.normalizedPwleEffects()) {
+                                return new PwleSegment(in);
+                            }
+                            // Fall through if the flag is not enabled.
                         default:
                             throw new IllegalStateException(
                                     "Unexpected vibration event type token in parcel.");
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 83c599e..8efbc9c 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -11085,6 +11085,12 @@
         public static final String SFPS_PERFORMANT_AUTH_ENABLED = "sfps_performant_auth_enabled_v2";
 
         /**
+         * Whether or not the UDFPS device is enabling the screen off unlock settings.
+         * @hide
+         */
+        public static final String SCREEN_OFF_UNLOCK_UDFPS_ENABLED = "screen_off_udfps_enabled";
+
+        /**
          * Whether or not debugging is enabled.
          * @hide
          */
diff --git a/core/java/android/security/advancedprotection/AdvancedProtectionManager.java b/core/java/android/security/advancedprotection/AdvancedProtectionManager.java
index 59dd680f..43b6ebe 100644
--- a/core/java/android/security/advancedprotection/AdvancedProtectionManager.java
+++ b/core/java/android/security/advancedprotection/AdvancedProtectionManager.java
@@ -42,7 +42,7 @@
 @FlaggedApi(Flags.FLAG_AAPM_API)
 @SystemService(Context.ADVANCED_PROTECTION_SERVICE)
 public class AdvancedProtectionManager {
-    private static final String TAG = "AdvancedProtectionM";
+    private static final String TAG = "AdvancedProtectionMgr";
 
     private final ConcurrentHashMap<Callback, IAdvancedProtectionCallback>
             mCallbackMap = new ConcurrentHashMap<>();
@@ -73,7 +73,7 @@
      * Registers a {@link Callback} to be notified of changes to the Advanced Protection state.
      *
      * <p>The provided callback will be called on the specified executor with the updated
-     * {@link AdvancedProtectionState}. Methods are called when the state changes, as well as once
+     * state. Methods are called when the state changes, as well as once
      * on initial registration.
      *
      * @param executor The executor of where the callback will execute.
diff --git a/core/java/android/security/forensic/ForensicEvent.java b/core/java/android/security/forensic/ForensicEvent.java
index 9cbc5ec..90906ed 100644
--- a/core/java/android/security/forensic/ForensicEvent.java
+++ b/core/java/android/security/forensic/ForensicEvent.java
@@ -61,6 +61,14 @@
         in.readMap(mKeyValuePairs, getClass().getClassLoader(), String.class, String.class);
     }
 
+    public String getType() {
+        return mType;
+    }
+
+    public Map<String, String> getKeyValuePairs() {
+        return mKeyValuePairs;
+    }
+
     @Override
     public void writeToParcel(@NonNull Parcel out, int flags) {
         out.writeString(mType);
diff --git a/core/java/android/security/responsible_apis_flags.aconfig b/core/java/android/security/responsible_apis_flags.aconfig
index 9bb1039..dec28c3 100644
--- a/core/java/android/security/responsible_apis_flags.aconfig
+++ b/core/java/android/security/responsible_apis_flags.aconfig
@@ -84,4 +84,13 @@
     namespace: "responsible_apis"
     description: "Prevent intent redirect attacks by aborting or throwing security exception"
     bug: "361143368"
-}
\ No newline at end of file
+}
+
+flag {
+    name: "enable_intent_matching_flags"
+    is_exported: true
+    namespace: "permissions"
+    is_fixed_read_only: true
+    description: "Applies intentMatchingFlags while matching intents to application components"
+    bug: "364354494"
+}
diff --git a/core/java/android/service/chooser/AdditionalContentContract.java b/core/java/android/service/chooser/AdditionalContentContract.java
index f679e8a..d4f36e4 100644
--- a/core/java/android/service/chooser/AdditionalContentContract.java
+++ b/core/java/android/service/chooser/AdditionalContentContract.java
@@ -16,13 +16,10 @@
 
 package android.service.chooser;
 
-import android.annotation.FlaggedApi;
-
 /**
  * Specifies constants used by Chooser when interacting with the additional content provider,
  * see {@link android.content.Intent#EXTRA_CHOOSER_ADDITIONAL_CONTENT_URI}.
  */
-@FlaggedApi(android.service.chooser.Flags.FLAG_CHOOSER_PAYLOAD_TOGGLING)
 public interface AdditionalContentContract {
 
     interface Columns {
diff --git a/core/java/android/service/contentsuggestions/ContentSuggestionsService.java b/core/java/android/service/contentsuggestions/ContentSuggestionsService.java
index 0b4739e..7f4e67a 100644
--- a/core/java/android/service/contentsuggestions/ContentSuggestionsService.java
+++ b/core/java/android/service/contentsuggestions/ContentSuggestionsService.java
@@ -39,6 +39,7 @@
 import android.os.RemoteException;
 import android.util.Log;
 import android.util.Slog;
+import android.window.TaskSnapshot;
 
 /**
  * @hide
@@ -62,10 +63,10 @@
 
     private final IContentSuggestionsService mInterface = new IContentSuggestionsService.Stub() {
         @Override
-        public void provideContextImage(int taskId, HardwareBuffer contextImage,
-                int colorSpaceId, Bundle imageContextRequestExtras) {
+        public void provideContextImage(int taskId, TaskSnapshot snapshot,
+                Bundle imageContextRequestExtras) {
             if (imageContextRequestExtras.containsKey(ContentSuggestionsManager.EXTRA_BITMAP)
-                    && contextImage != null) {
+                    && snapshot != null) {
                 throw new IllegalArgumentException("Two bitmaps provided; expected one.");
             }
 
@@ -74,13 +75,18 @@
                 wrappedBuffer = imageContextRequestExtras.getParcelable(
                         ContentSuggestionsManager.EXTRA_BITMAP, android.graphics.Bitmap.class);
             } else {
-                if (contextImage != null) {
-                    ColorSpace colorSpace = null;
+                if (snapshot != null) {
+                    final HardwareBuffer snapshotBuffer = snapshot.getHardwareBuffer();
+                    ColorSpace colorSpace = snapshot.getColorSpace();
+                    int colorSpaceId = 0;
+                    if (colorSpace != null) {
+                        colorSpaceId = colorSpace.getId();
+                    }
                     if (colorSpaceId >= 0 && colorSpaceId < ColorSpace.Named.values().length) {
                         colorSpace = ColorSpace.get(ColorSpace.Named.values()[colorSpaceId]);
                     }
-                    wrappedBuffer = Bitmap.wrapHardwareBuffer(contextImage, colorSpace);
-                    contextImage.close();
+                    wrappedBuffer = Bitmap.wrapHardwareBuffer(snapshotBuffer, colorSpace);
+                    snapshotBuffer.close();
                 }
             }
 
diff --git a/core/java/android/service/contentsuggestions/IContentSuggestionsService.aidl b/core/java/android/service/contentsuggestions/IContentSuggestionsService.aidl
index d8f23e7..398fa37 100644
--- a/core/java/android/service/contentsuggestions/IContentSuggestionsService.aidl
+++ b/core/java/android/service/contentsuggestions/IContentSuggestionsService.aidl
@@ -20,7 +20,7 @@
 import android.app.contentsuggestions.ISelectionsCallback;
 import android.app.contentsuggestions.ClassificationsRequest;
 import android.app.contentsuggestions.SelectionsRequest;
-import android.hardware.HardwareBuffer;
+import android.window.TaskSnapshot;
 import android.os.Bundle;
 
 /**
@@ -31,8 +31,7 @@
 oneway interface IContentSuggestionsService {
     void provideContextImage(
             int taskId,
-            in HardwareBuffer contextImage,
-            int colorSpaceId,
+            in TaskSnapshot snapshot,
             in Bundle imageContextRequestExtras);
     void suggestContentSelections(
             in SelectionsRequest request,
diff --git a/core/java/android/service/notification/SystemZenRules.java b/core/java/android/service/notification/SystemZenRules.java
index 1d18643..ebb8569 100644
--- a/core/java/android/service/notification/SystemZenRules.java
+++ b/core/java/android/service/notification/SystemZenRules.java
@@ -19,6 +19,7 @@
 import android.annotation.FlaggedApi;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.StringRes;
 import android.app.AutomaticZenRule;
 import android.app.Flags;
 import android.content.Context;
@@ -122,7 +123,7 @@
     public static String getTriggerDescriptionForScheduleTime(Context context,
             @NonNull ScheduleInfo schedule) {
         final StringBuilder sb = new StringBuilder();
-        String daysSummary = getShortDaysSummary(context, schedule);
+        String daysSummary = getDaysOfWeekShort(context, schedule);
         if (daysSummary == null) {
             // no use outputting times without dates
             return null;
@@ -135,11 +136,35 @@
     }
 
     /**
-     * Returns an ordered summarized list of the days on which this schedule applies, with
-     * adjacent days grouped together ("Sun-Wed" instead of "Sun,Mon,Tue,Wed").
+     * Returns a short, ordered summarized list of the days on which this schedule applies, using
+     * abbreviated week days, with adjacent days grouped together ("Sun-Wed" instead of
+     * "Sun,Mon,Tue,Wed").
      */
     @Nullable
-    public static String getShortDaysSummary(Context context, @NonNull ScheduleInfo schedule) {
+    public static String getDaysOfWeekShort(Context context, @NonNull ScheduleInfo schedule) {
+        return getDaysSummary(context, R.string.zen_mode_trigger_summary_range_symbol_combination,
+                new SimpleDateFormat("EEE", getLocale(context)), schedule);
+    }
+
+    /**
+     * Returns a string representing the days on which this schedule applies, using full week days,
+     * with adjacent days grouped together (e.g. "Sunday to Wednesday" instead of
+     * "Sunday,Monday,Tuesday,Wednesday").
+     */
+    @Nullable
+    public static String getDaysOfWeekFull(Context context, @NonNull ScheduleInfo schedule) {
+        return getDaysSummary(context, R.string.zen_mode_trigger_summary_range_words,
+                new SimpleDateFormat("EEEE", getLocale(context)), schedule);
+    }
+
+    /**
+     * Returns an ordered summarized list of the days on which this schedule applies, with
+     * adjacent days grouped together. The formatting of each individual day of week is done with
+     * the provided {@link SimpleDateFormat}.
+     */
+    @Nullable
+    private static String getDaysSummary(Context context, @StringRes int rangeFormatResId,
+            SimpleDateFormat dayOfWeekFormat, @NonNull ScheduleInfo schedule) {
         // Compute a list of days with contiguous days grouped together, for example: "Sun-Thu" or
         // "Sun-Mon,Wed,Fri"
         final int[] days = schedule.days;
@@ -197,19 +222,18 @@
                                 context.getString(R.string.zen_mode_trigger_summary_divider_text));
                     }
 
-                    SimpleDateFormat dayFormat = new SimpleDateFormat("EEE", getLocale(context));
                     if (startDay == lastSeenDay) {
                         // last group was only one day
                         cStart.set(Calendar.DAY_OF_WEEK, daysOfWeek[startDay]);
-                        sb.append(dayFormat.format(cStart.getTime()));
+                        sb.append(dayOfWeekFormat.format(cStart.getTime()));
                     } else {
                         // last group was a contiguous group of days, so group them together
                         cStart.set(Calendar.DAY_OF_WEEK, daysOfWeek[startDay]);
                         cEnd.set(Calendar.DAY_OF_WEEK, daysOfWeek[lastSeenDay]);
                         sb.append(context.getString(
-                                R.string.zen_mode_trigger_summary_range_symbol_combination,
-                                dayFormat.format(cStart.getTime()),
-                                dayFormat.format(cEnd.getTime())));
+                                rangeFormatResId,
+                                dayOfWeekFormat.format(cStart.getTime()),
+                                dayOfWeekFormat.format(cEnd.getTime())));
                     }
                 }
             }
diff --git a/core/java/android/service/wearable/IWearableSensingService.aidl b/core/java/android/service/wearable/IWearableSensingService.aidl
index 9d9cacf..e6624ca 100644
--- a/core/java/android/service/wearable/IWearableSensingService.aidl
+++ b/core/java/android/service/wearable/IWearableSensingService.aidl
@@ -18,6 +18,7 @@
 
 import android.app.ambientcontext.AmbientContextEventRequest;
 import android.app.wearable.IWearableSensingCallback;
+import android.os.ParcelFileDescriptor;
 import android.os.PersistableBundle;
 import android.os.RemoteCallback;
 import android.os.SharedMemory;
@@ -30,6 +31,8 @@
  */
 oneway interface IWearableSensingService {
     void provideSecureConnection(in ParcelFileDescriptor parcelFileDescriptor, in IWearableSensingCallback wearableSensingCallback, in RemoteCallback statusCallback);
+    void provideConcurrentSecureConnection(in ParcelFileDescriptor parcelFileDescriptor, in PersistableBundle metadata, in IWearableSensingCallback wearableSensingCallback, in RemoteCallback statusCallback);
+    void provideReadOnlyParcelFileDescriptor(in ParcelFileDescriptor parcelFileDescriptor, in PersistableBundle metadata, in RemoteCallback statusCallback);
     void provideDataStream(in ParcelFileDescriptor parcelFileDescriptor, in IWearableSensingCallback wearableSensingCallback, in RemoteCallback statusCallback);
     void provideData(in PersistableBundle data, in SharedMemory sharedMemory, in RemoteCallback callback);
     void registerDataRequestObserver(int dataType, in RemoteCallback dataRequestCallback, int dataRequestObserverId, in String packageName, in RemoteCallback statusCallback);
diff --git a/core/java/android/service/wearable/WearableSensingDataRequester.java b/core/java/android/service/wearable/WearableSensingDataRequester.java
index 5a8104f..d6133f2 100644
--- a/core/java/android/service/wearable/WearableSensingDataRequester.java
+++ b/core/java/android/service/wearable/WearableSensingDataRequester.java
@@ -16,11 +16,9 @@
 
 package android.service.wearable;
 
-import android.annotation.FlaggedApi;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
-import android.app.wearable.Flags;
 import android.app.wearable.WearableSensingDataRequest;
 
 import java.lang.annotation.Retention;
@@ -32,7 +30,6 @@
  *
  * @hide
  */
-@FlaggedApi(Flags.FLAG_ENABLE_DATA_REQUEST_OBSERVER_API)
 @SystemApi
 public interface WearableSensingDataRequester {
 
diff --git a/core/java/android/service/wearable/WearableSensingService.java b/core/java/android/service/wearable/WearableSensingService.java
index 3735c43..47ec080 100644
--- a/core/java/android/service/wearable/WearableSensingService.java
+++ b/core/java/android/service/wearable/WearableSensingService.java
@@ -63,16 +63,15 @@
 
 /**
  * Abstract base class for sensing with wearable devices. An example of this is {@link
- *AmbientContextEvent} detection.
+ * AmbientContextEvent} detection.
  *
- * <p> A service that provides requested sensing events to the system, such as a {@link
- *AmbientContextEvent}. The system's default WearableSensingService implementation is configured in
- * {@code config_defaultWearableSensingService}. If this config has no value, a stub is
- * returned.
+ * <p>A service that provides requested sensing events to the system, such as a {@link
+ * AmbientContextEvent}. The system's default WearableSensingService implementation is configured in
+ * {@code config_defaultWearableSensingService}. If this config has no value, a stub is returned.
  *
- * <p> An implementation of a WearableSensingService should be an isolated service. Using the
- * "isolatedProcess=true" attribute in the service's configurations. </p>
- **
+ * <p>An implementation of a WearableSensingService should be an isolated service. Using the
+ * "isolatedProcess=true" attribute in the service's configurations.
+ *
  * <pre>
  * {@literal
  * <service android:name=".YourWearableSensingService"
@@ -82,7 +81,7 @@
  * </pre>
  *
  * <p>The use of "Wearable" here is not the same as the Android Wear platform and should be treated
- * separately. </p>
+ * separately.
  *
  * @hide
  */
@@ -108,9 +107,9 @@
 
     /**
      * The {@link Intent} that must be declared as handled by the service. To be supported, the
-     * service must also require the
-     * {@link android.Manifest.permission#BIND_WEARABLE_SENSING_SERVICE}
-     * permission so that other applications can not abuse it.
+     * service must also require the {@link
+     * android.Manifest.permission#BIND_WEARABLE_SENSING_SERVICE} permission so that other
+     * applications can not abuse it.
      */
     public static final String SERVICE_INTERFACE =
             "android.service.wearable.WearableSensingService";
@@ -145,6 +144,35 @@
 
                 /** {@inheritDoc} */
                 @Override
+                public void provideConcurrentSecureConnection(
+                        ParcelFileDescriptor secureWearableConnection,
+                        PersistableBundle metadata,
+                        IWearableSensingCallback wearableSensingCallback,
+                        RemoteCallback callback) {
+                    Objects.requireNonNull(secureWearableConnection);
+                    Objects.requireNonNull(metadata);
+                    if (wearableSensingCallback != null) {
+                        mWearableSensingCallback = wearableSensingCallback;
+                    }
+                    Consumer<Integer> consumer = createWearableStatusConsumer(callback);
+                    WearableSensingService.this.onSecureConnectionProvided(
+                            secureWearableConnection, metadata, consumer);
+                }
+
+                /** {@inheritDoc} */
+                @Override
+                public void provideReadOnlyParcelFileDescriptor(
+                        ParcelFileDescriptor parcelFileDescriptor,
+                        PersistableBundle metadata,
+                        RemoteCallback callback) {
+                    Objects.requireNonNull(parcelFileDescriptor);
+                    Consumer<Integer> consumer = createWearableStatusConsumer(callback);
+                    WearableSensingService.this.onReadOnlyParcelFileDescriptorProvided(
+                            parcelFileDescriptor, metadata, consumer);
+                }
+
+                /** {@inheritDoc} */
+                @Override
                 public void provideDataStream(
                         ParcelFileDescriptor parcelFileDescriptor,
                         IWearableSensingCallback wearableSensingCallback,
@@ -339,13 +367,13 @@
 
     /**
      * Called when a secure connection to the wearable is available. See {@link
-     * WearableSensingManager#provideConnection(ParcelFileDescriptor, Executor, Consumer)}
-     * for details about the secure connection.
+     * WearableSensingManager#provideConnection(ParcelFileDescriptor, Executor, Consumer)} for
+     * details about the secure connection.
      *
      * <p>When the {@code secureWearableConnection} is closed, the system will send a {@link
      * WearableSensingManager#STATUS_CHANNEL_ERROR} status code to the status consumer provided by
-     * the caller of {@link WearableSensingManager#provideConnection(ParcelFileDescriptor,
-     * Executor, Consumer)}.
+     * the caller of {@link WearableSensingManager#provideConnection(ParcelFileDescriptor, Executor,
+     * Consumer)}.
      *
      * <p>The implementing class should override this method. It should return an appropriate status
      * code via {@code statusConsumer} after receiving the {@code secureWearableConnection}.
@@ -353,7 +381,6 @@
      * @param secureWearableConnection The secure connection to the wearable.
      * @param statusConsumer The consumer for the service status.
      */
-    @FlaggedApi(Flags.FLAG_ENABLE_PROVIDE_WEARABLE_CONNECTION_API)
     @BinderThread
     public void onSecureConnectionProvided(
             @NonNull ParcelFileDescriptor secureWearableConnection,
@@ -362,6 +389,46 @@
     }
 
     /**
+     * Called when a secure connection to the wearable is available.
+     *
+     * @param secureWearableConnection The secure connection to the wearable.
+     * @param metadata Metadata related to the provided connection.
+     * @param statusConsumer The consumer for the service status.
+     * @see #onSecureConnectionProvided(ParcelFileDescriptor, Consumer)
+     */
+    @FlaggedApi(Flags.FLAG_ENABLE_CONCURRENT_WEARABLE_CONNECTIONS)
+    @BinderThread
+    public void onSecureConnectionProvided(
+            @NonNull ParcelFileDescriptor secureWearableConnection,
+            @NonNull PersistableBundle metadata,
+            @NonNull Consumer<Integer> statusConsumer) {
+        statusConsumer.accept(WearableSensingManager.STATUS_UNSUPPORTED_OPERATION);
+    }
+
+    /**
+     * Called when a read-only {@link ParcelFileDescriptor} is provided.
+     *
+     * <p>It is up to the implementation to close the {@link ParcelFileDescriptor} when it is
+     * finished.
+     *
+     * <p>The implementation should return one of the status code defined in the {@link
+     * WearableSensingManager} via the {@code statusConsumer}.
+     *
+     * @param parcelFileDescriptor The provided read-only {@link ParcelFileDescriptor}
+     * @param metadata The metadata provided along with the {@code parcelFileDescriptor}
+     * @param statusConsumer the consumer for the status code
+     */
+    @FlaggedApi(Flags.FLAG_ENABLE_PROVIDE_READ_ONLY_PFD)
+    @BinderThread
+    public void onReadOnlyParcelFileDescriptorProvided(
+            @NonNull ParcelFileDescriptor parcelFileDescriptor,
+            @NonNull PersistableBundle metadata,
+            @NonNull Consumer<Integer> statusConsumer) {
+        // placeholder implementation
+        statusConsumer.accept(WearableSensingManager.STATUS_UNSUPPORTED_OPERATION);
+    }
+
+    /**
      * Called when a data stream to the wearable is provided. This data stream can be used to obtain
      * data from a wearable device. It is up to the implementation to maintain the data stream and
      * close the data stream when it is finished.
@@ -370,7 +437,8 @@
      * @param statusConsumer the consumer for the service status.
      */
     @BinderThread
-    public abstract void onDataStreamProvided(@NonNull ParcelFileDescriptor parcelFileDescriptor,
+    public abstract void onDataStreamProvided(
+            @NonNull ParcelFileDescriptor parcelFileDescriptor,
             @NonNull Consumer<Integer> statusConsumer);
 
     /**
@@ -419,7 +487,6 @@
      * @param statusConsumer the consumer for the status of the data request observer registration.
      *     This is different from the status for each data request.
      */
-    @FlaggedApi(Flags.FLAG_ENABLE_DATA_REQUEST_OBSERVER_API)
     @BinderThread
     public void onDataRequestObserverRegistered(
             int dataType,
@@ -447,7 +514,6 @@
      * @param statusConsumer the consumer for the status of the data request observer
      *     unregistration. This is different from the status for each data request.
      */
-    @FlaggedApi(Flags.FLAG_ENABLE_DATA_REQUEST_OBSERVER_API)
     @BinderThread
     public void onDataRequestObserverUnregistered(
             int dataType,
@@ -514,16 +580,16 @@
 
     /**
      * Called when hotword audio data sent to the {@code hotwordAudioConsumer} in {@link
-     * #onStartHotwordRecognition(Consumer, Consumer)} is accepted by the
-     * {@link android.service.voice.HotwordDetectionService} as valid hotword.
+     * #onStartHotwordRecognition(Consumer, Consumer)} is accepted by the {@link
+     * android.service.voice.HotwordDetectionService} as valid hotword.
      *
      * <p>After the implementation of this class sends the hotword audio data to the {@code
-     * hotwordAudioConsumer} in {@link #onStartHotwordRecognition(Consumer,
-     * Consumer)}, the system will forward the data into {@link
-     * android.service.voice.HotwordDetectionService} (which runs in an isolated process) for
-     * second-stage hotword detection. If accepted as valid hotword there, this method will be
-     * called, and then the system will send the data to the currently active {@link
-     * android.service.voice.AlwaysOnHotwordDetector} (which may not run in an isolated process).
+     * hotwordAudioConsumer} in {@link #onStartHotwordRecognition(Consumer, Consumer)}, the system
+     * will forward the data into {@link android.service.voice.HotwordDetectionService} (which runs
+     * in an isolated process) for second-stage hotword detection. If accepted as valid hotword
+     * there, this method will be called, and then the system will send the data to the currently
+     * active {@link android.service.voice.AlwaysOnHotwordDetector} (which may not run in an
+     * isolated process).
      *
      * <p>This method is expected to be overridden by a derived class. The implementation must
      * request the wearable to turn on the microphone indicator to notify the user that audio data
@@ -554,17 +620,16 @@
     /**
      * Called when a client app requests starting detection of the events in the request. The
      * implementation should keep track of whether the user has explicitly consented to detecting
-     * the events using on-going ambient sensor (e.g. microphone), and agreed to share the
-     * detection results with this client app. If the user has not consented, the detection
-     * should not start, and the statusConsumer should get a response with STATUS_ACCESS_DENIED.
-     * If the user has made the consent and the underlying services are available, the
-     * implementation should start detection and provide detected events to the
-     * detectionResultConsumer. If the type of event needs immediate attention, the implementation
-     * should send result as soon as detected. Otherwise, the implementation can batch response.
-     * The ongoing detection will keep running, until onStopDetection is called. If there were
-     * previously requested detections from the same package, regardless of the type of events in
-     * the request, the previous request will be replaced with the new request and pending events
-     * are discarded.
+     * the events using on-going ambient sensor (e.g. microphone), and agreed to share the detection
+     * results with this client app. If the user has not consented, the detection should not start,
+     * and the statusConsumer should get a response with STATUS_ACCESS_DENIED. If the user has made
+     * the consent and the underlying services are available, the implementation should start
+     * detection and provide detected events to the detectionResultConsumer. If the type of event
+     * needs immediate attention, the implementation should send result as soon as detected.
+     * Otherwise, the implementation can batch response. The ongoing detection will keep running,
+     * until onStopDetection is called. If there were previously requested detections from the same
+     * package, regardless of the type of events in the request, the previous request will be
+     * replaced with the new request and pending events are discarded.
      *
      * @param request The request with events to detect.
      * @param packageName the requesting app's package name
@@ -572,7 +637,8 @@
      * @param detectionResultConsumer the consumer for the detected event
      */
     @BinderThread
-    public abstract void onStartDetection(@NonNull AmbientContextEventRequest request,
+    public abstract void onStartDetection(
+            @NonNull AmbientContextEventRequest request,
             @NonNull String packageName,
             @NonNull Consumer<AmbientContextDetectionServiceStatus> statusConsumer,
             @NonNull Consumer<AmbientContextDetectionResult> detectionResultConsumer);
@@ -585,16 +651,17 @@
     public abstract void onStopDetection(@NonNull String packageName);
 
     /**
-     * Called when a query for the detection status occurs. The implementation should check
-     * the detection status of the requested events for the package, and provide results in a
-     * {@link AmbientContextDetectionServiceStatus} for the consumer.
+     * Called when a query for the detection status occurs. The implementation should check the
+     * detection status of the requested events for the package, and provide results in a {@link
+     * AmbientContextDetectionServiceStatus} for the consumer.
      *
      * @param eventTypes The events to check for status.
      * @param packageName the requesting app's package name
      * @param consumer the consumer for the query results
      */
     @BinderThread
-    public abstract void onQueryServiceStatus(@NonNull Set<Integer> eventTypes,
+    public abstract void onQueryServiceStatus(
+            @NonNull Set<Integer> eventTypes,
             @NonNull String packageName,
             @NonNull Consumer<AmbientContextDetectionServiceStatus> consumer);
 
@@ -693,6 +760,4 @@
             statusCallback.sendResult(bundle);
         };
     }
-
-
 }
diff --git a/core/java/android/tracing/TEST_MAPPING b/core/java/android/tracing/TEST_MAPPING
index b51d19d..545ba11 100644
--- a/core/java/android/tracing/TEST_MAPPING
+++ b/core/java/android/tracing/TEST_MAPPING
@@ -1,5 +1,5 @@
 {
-  "postsubmit": [
+  "presubmit": [
     {
       "name": "TracingTests"
     },
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index 59c6653..25d2246 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -16,7 +16,6 @@
 
 package android.view;
 
-import static android.inputmethodservice.InputMethodService.ENABLE_HIDE_IME_CAPTION_BAR;
 import static android.os.Trace.TRACE_TAG_VIEW;
 import static android.view.InsetsControllerProto.CONTROL;
 import static android.view.InsetsControllerProto.STATE;
@@ -1083,7 +1082,7 @@
         }
     }
 
-    boolean isPredictiveBackImeHideAnimInProgress() {
+    public boolean isPredictiveBackImeHideAnimInProgress() {
         return mIsPredictiveBackImeHideAnimInProgress;
     }
 
@@ -2148,9 +2147,6 @@
 
     @Override
     public void setImeCaptionBarInsetsHeight(int height) {
-        if (!ENABLE_HIDE_IME_CAPTION_BAR) {
-            return;
-        }
         Rect newFrame = new Rect(mFrame.left, mFrame.bottom - height, mFrame.right, mFrame.bottom);
         InsetsSource source = mState.peekSource(ID_IME_CAPTION_BAR);
         if (mImeCaptionBarInsetsHeight != height
diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java
index 01015ea..dddc408 100644
--- a/core/java/android/view/KeyEvent.java
+++ b/core/java/android/view/KeyEvent.java
@@ -954,6 +954,331 @@
     @TestApi
     public static final int LAST_KEYCODE = KEYCODE_SCREENSHOT;
 
+    /** @hide */
+    @IntDef(prefix = {"KEYCODE_"}, value = {
+            KEYCODE_UNKNOWN,
+            KEYCODE_SOFT_LEFT,
+            KEYCODE_SOFT_RIGHT,
+            KEYCODE_HOME,
+            KEYCODE_BACK,
+            KEYCODE_CALL,
+            KEYCODE_ENDCALL,
+            KEYCODE_0,
+            KEYCODE_1,
+            KEYCODE_2,
+            KEYCODE_3,
+            KEYCODE_4,
+            KEYCODE_5,
+            KEYCODE_6,
+            KEYCODE_7,
+            KEYCODE_8,
+            KEYCODE_9,
+            KEYCODE_STAR,
+            KEYCODE_POUND,
+            KEYCODE_DPAD_UP,
+            KEYCODE_DPAD_DOWN,
+            KEYCODE_DPAD_LEFT,
+            KEYCODE_DPAD_RIGHT,
+            KEYCODE_DPAD_CENTER,
+            KEYCODE_VOLUME_UP,
+            KEYCODE_VOLUME_DOWN,
+            KEYCODE_POWER,
+            KEYCODE_CAMERA,
+            KEYCODE_CLEAR,
+            KEYCODE_A,
+            KEYCODE_B,
+            KEYCODE_C,
+            KEYCODE_D,
+            KEYCODE_E,
+            KEYCODE_F,
+            KEYCODE_G,
+            KEYCODE_H,
+            KEYCODE_I,
+            KEYCODE_J,
+            KEYCODE_K,
+            KEYCODE_L,
+            KEYCODE_M,
+            KEYCODE_N,
+            KEYCODE_O,
+            KEYCODE_P,
+            KEYCODE_Q,
+            KEYCODE_R,
+            KEYCODE_S,
+            KEYCODE_T,
+            KEYCODE_U,
+            KEYCODE_V,
+            KEYCODE_W,
+            KEYCODE_X,
+            KEYCODE_Y,
+            KEYCODE_Z,
+            KEYCODE_COMMA,
+            KEYCODE_PERIOD,
+            KEYCODE_ALT_LEFT,
+            KEYCODE_ALT_RIGHT,
+            KEYCODE_SHIFT_LEFT,
+            KEYCODE_SHIFT_RIGHT,
+            KEYCODE_TAB,
+            KEYCODE_SPACE,
+            KEYCODE_SYM,
+            KEYCODE_EXPLORER,
+            KEYCODE_ENVELOPE,
+            KEYCODE_ENTER,
+            KEYCODE_DEL,
+            KEYCODE_GRAVE,
+            KEYCODE_MINUS,
+            KEYCODE_EQUALS,
+            KEYCODE_LEFT_BRACKET,
+            KEYCODE_RIGHT_BRACKET,
+            KEYCODE_BACKSLASH,
+            KEYCODE_SEMICOLON,
+            KEYCODE_APOSTROPHE,
+            KEYCODE_SLASH,
+            KEYCODE_AT,
+            KEYCODE_NUM,
+            KEYCODE_HEADSETHOOK,
+            KEYCODE_FOCUS,
+            KEYCODE_PLUS,
+            KEYCODE_MENU,
+            KEYCODE_NOTIFICATION,
+            KEYCODE_SEARCH,
+            KEYCODE_MEDIA_PLAY_PAUSE,
+            KEYCODE_MEDIA_STOP,
+            KEYCODE_MEDIA_NEXT,
+            KEYCODE_MEDIA_PREVIOUS,
+            KEYCODE_MEDIA_REWIND,
+            KEYCODE_MEDIA_FAST_FORWARD,
+            KEYCODE_MUTE,
+            KEYCODE_PAGE_UP,
+            KEYCODE_PAGE_DOWN,
+            KEYCODE_PICTSYMBOLS,
+            KEYCODE_SWITCH_CHARSET,
+            KEYCODE_BUTTON_A,
+            KEYCODE_BUTTON_B,
+            KEYCODE_BUTTON_C,
+            KEYCODE_BUTTON_X,
+            KEYCODE_BUTTON_Y,
+            KEYCODE_BUTTON_Z,
+            KEYCODE_BUTTON_L1,
+            KEYCODE_BUTTON_R1,
+            KEYCODE_BUTTON_L2,
+            KEYCODE_BUTTON_R2,
+            KEYCODE_BUTTON_THUMBL,
+            KEYCODE_BUTTON_THUMBR,
+            KEYCODE_BUTTON_START,
+            KEYCODE_BUTTON_SELECT,
+            KEYCODE_BUTTON_MODE,
+            KEYCODE_ESCAPE,
+            KEYCODE_FORWARD_DEL,
+            KEYCODE_CTRL_LEFT,
+            KEYCODE_CTRL_RIGHT,
+            KEYCODE_CAPS_LOCK,
+            KEYCODE_SCROLL_LOCK,
+            KEYCODE_META_LEFT,
+            KEYCODE_META_RIGHT,
+            KEYCODE_FUNCTION,
+            KEYCODE_SYSRQ,
+            KEYCODE_BREAK,
+            KEYCODE_MOVE_HOME,
+            KEYCODE_MOVE_END,
+            KEYCODE_INSERT,
+            KEYCODE_FORWARD,
+            KEYCODE_MEDIA_PLAY,
+            KEYCODE_MEDIA_PAUSE,
+            KEYCODE_MEDIA_CLOSE,
+            KEYCODE_MEDIA_EJECT,
+            KEYCODE_MEDIA_RECORD,
+            KEYCODE_F1,
+            KEYCODE_F2,
+            KEYCODE_F3,
+            KEYCODE_F4,
+            KEYCODE_F5,
+            KEYCODE_F6,
+            KEYCODE_F7,
+            KEYCODE_F8,
+            KEYCODE_F9,
+            KEYCODE_F10,
+            KEYCODE_F11,
+            KEYCODE_F12,
+            KEYCODE_NUM_LOCK,
+            KEYCODE_NUMPAD_0,
+            KEYCODE_NUMPAD_1,
+            KEYCODE_NUMPAD_2,
+            KEYCODE_NUMPAD_3,
+            KEYCODE_NUMPAD_4,
+            KEYCODE_NUMPAD_5,
+            KEYCODE_NUMPAD_6,
+            KEYCODE_NUMPAD_7,
+            KEYCODE_NUMPAD_8,
+            KEYCODE_NUMPAD_9,
+            KEYCODE_NUMPAD_DIVIDE,
+            KEYCODE_NUMPAD_MULTIPLY,
+            KEYCODE_NUMPAD_SUBTRACT,
+            KEYCODE_NUMPAD_ADD,
+            KEYCODE_NUMPAD_DOT,
+            KEYCODE_NUMPAD_COMMA,
+            KEYCODE_NUMPAD_ENTER,
+            KEYCODE_NUMPAD_EQUALS,
+            KEYCODE_NUMPAD_LEFT_PAREN,
+            KEYCODE_NUMPAD_RIGHT_PAREN,
+            KEYCODE_VOLUME_MUTE,
+            KEYCODE_INFO,
+            KEYCODE_CHANNEL_UP,
+            KEYCODE_CHANNEL_DOWN,
+            KEYCODE_ZOOM_IN,
+            KEYCODE_ZOOM_OUT,
+            KEYCODE_TV,
+            KEYCODE_WINDOW,
+            KEYCODE_GUIDE,
+            KEYCODE_DVR,
+            KEYCODE_BOOKMARK,
+            KEYCODE_CAPTIONS,
+            KEYCODE_SETTINGS,
+            KEYCODE_TV_POWER,
+            KEYCODE_TV_INPUT,
+            KEYCODE_STB_POWER,
+            KEYCODE_STB_INPUT,
+            KEYCODE_AVR_POWER,
+            KEYCODE_AVR_INPUT,
+            KEYCODE_PROG_RED,
+            KEYCODE_PROG_GREEN,
+            KEYCODE_PROG_YELLOW,
+            KEYCODE_PROG_BLUE,
+            KEYCODE_APP_SWITCH,
+            KEYCODE_BUTTON_1,
+            KEYCODE_BUTTON_2,
+            KEYCODE_BUTTON_3,
+            KEYCODE_BUTTON_4,
+            KEYCODE_BUTTON_5,
+            KEYCODE_BUTTON_6,
+            KEYCODE_BUTTON_7,
+            KEYCODE_BUTTON_8,
+            KEYCODE_BUTTON_9,
+            KEYCODE_BUTTON_10,
+            KEYCODE_BUTTON_11,
+            KEYCODE_BUTTON_12,
+            KEYCODE_BUTTON_13,
+            KEYCODE_BUTTON_14,
+            KEYCODE_BUTTON_15,
+            KEYCODE_BUTTON_16,
+            KEYCODE_LANGUAGE_SWITCH,
+            KEYCODE_MANNER_MODE,
+            KEYCODE_3D_MODE,
+            KEYCODE_CONTACTS,
+            KEYCODE_CALENDAR,
+            KEYCODE_MUSIC,
+            KEYCODE_CALCULATOR,
+            KEYCODE_ZENKAKU_HANKAKU,
+            KEYCODE_EISU,
+            KEYCODE_MUHENKAN,
+            KEYCODE_HENKAN,
+            KEYCODE_KATAKANA_HIRAGANA,
+            KEYCODE_YEN,
+            KEYCODE_RO,
+            KEYCODE_KANA,
+            KEYCODE_ASSIST,
+            KEYCODE_BRIGHTNESS_DOWN,
+            KEYCODE_BRIGHTNESS_UP,
+            KEYCODE_MEDIA_AUDIO_TRACK,
+            KEYCODE_SLEEP,
+            KEYCODE_WAKEUP,
+            KEYCODE_PAIRING,
+            KEYCODE_MEDIA_TOP_MENU,
+            KEYCODE_11,
+            KEYCODE_12,
+            KEYCODE_LAST_CHANNEL,
+            KEYCODE_TV_DATA_SERVICE,
+            KEYCODE_VOICE_ASSIST,
+            KEYCODE_TV_RADIO_SERVICE,
+            KEYCODE_TV_TELETEXT,
+            KEYCODE_TV_NUMBER_ENTRY,
+            KEYCODE_TV_TERRESTRIAL_ANALOG,
+            KEYCODE_TV_TERRESTRIAL_DIGITAL,
+            KEYCODE_TV_SATELLITE,
+            KEYCODE_TV_SATELLITE_BS,
+            KEYCODE_TV_SATELLITE_CS,
+            KEYCODE_TV_SATELLITE_SERVICE,
+            KEYCODE_TV_NETWORK,
+            KEYCODE_TV_ANTENNA_CABLE,
+            KEYCODE_TV_INPUT_HDMI_1,
+            KEYCODE_TV_INPUT_HDMI_2,
+            KEYCODE_TV_INPUT_HDMI_3,
+            KEYCODE_TV_INPUT_HDMI_4,
+            KEYCODE_TV_INPUT_COMPOSITE_1,
+            KEYCODE_TV_INPUT_COMPOSITE_2,
+            KEYCODE_TV_INPUT_COMPONENT_1,
+            KEYCODE_TV_INPUT_COMPONENT_2,
+            KEYCODE_TV_INPUT_VGA_1,
+            KEYCODE_TV_AUDIO_DESCRIPTION,
+            KEYCODE_TV_AUDIO_DESCRIPTION_MIX_UP,
+            KEYCODE_TV_AUDIO_DESCRIPTION_MIX_DOWN,
+            KEYCODE_TV_ZOOM_MODE,
+            KEYCODE_TV_CONTENTS_MENU,
+            KEYCODE_TV_MEDIA_CONTEXT_MENU,
+            KEYCODE_TV_TIMER_PROGRAMMING,
+            KEYCODE_HELP,
+            KEYCODE_NAVIGATE_PREVIOUS,
+            KEYCODE_NAVIGATE_NEXT,
+            KEYCODE_NAVIGATE_IN,
+            KEYCODE_NAVIGATE_OUT,
+            KEYCODE_STEM_PRIMARY,
+            KEYCODE_STEM_1,
+            KEYCODE_STEM_2,
+            KEYCODE_STEM_3,
+            KEYCODE_DPAD_UP_LEFT,
+            KEYCODE_DPAD_DOWN_LEFT,
+            KEYCODE_DPAD_UP_RIGHT,
+            KEYCODE_DPAD_DOWN_RIGHT,
+            KEYCODE_MEDIA_SKIP_FORWARD,
+            KEYCODE_MEDIA_SKIP_BACKWARD,
+            KEYCODE_MEDIA_STEP_FORWARD,
+            KEYCODE_MEDIA_STEP_BACKWARD,
+            KEYCODE_SOFT_SLEEP,
+            KEYCODE_CUT,
+            KEYCODE_COPY,
+            KEYCODE_PASTE,
+            KEYCODE_SYSTEM_NAVIGATION_UP,
+            KEYCODE_SYSTEM_NAVIGATION_DOWN,
+            KEYCODE_SYSTEM_NAVIGATION_LEFT,
+            KEYCODE_SYSTEM_NAVIGATION_RIGHT,
+            KEYCODE_ALL_APPS,
+            KEYCODE_REFRESH,
+            KEYCODE_THUMBS_UP,
+            KEYCODE_THUMBS_DOWN,
+            KEYCODE_PROFILE_SWITCH,
+            KEYCODE_VIDEO_APP_1,
+            KEYCODE_VIDEO_APP_2,
+            KEYCODE_VIDEO_APP_3,
+            KEYCODE_VIDEO_APP_4,
+            KEYCODE_VIDEO_APP_5,
+            KEYCODE_VIDEO_APP_6,
+            KEYCODE_VIDEO_APP_7,
+            KEYCODE_VIDEO_APP_8,
+            KEYCODE_FEATURED_APP_1,
+            KEYCODE_FEATURED_APP_2,
+            KEYCODE_FEATURED_APP_3,
+            KEYCODE_FEATURED_APP_4,
+            KEYCODE_DEMO_APP_1,
+            KEYCODE_DEMO_APP_2,
+            KEYCODE_DEMO_APP_3,
+            KEYCODE_DEMO_APP_4,
+            KEYCODE_KEYBOARD_BACKLIGHT_DOWN,
+            KEYCODE_KEYBOARD_BACKLIGHT_UP,
+            KEYCODE_KEYBOARD_BACKLIGHT_TOGGLE,
+            KEYCODE_STYLUS_BUTTON_PRIMARY,
+            KEYCODE_STYLUS_BUTTON_SECONDARY,
+            KEYCODE_STYLUS_BUTTON_TERTIARY,
+            KEYCODE_STYLUS_BUTTON_TAIL,
+            KEYCODE_RECENT_APPS,
+            KEYCODE_MACRO_1,
+            KEYCODE_MACRO_2,
+            KEYCODE_MACRO_3,
+            KEYCODE_MACRO_4,
+            KEYCODE_EMOJI_PICKER,
+            KEYCODE_SCREENSHOT,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    @interface KeyCode {}
+
     // NOTE: If you add a new keycode here you must also add it to:
     //  isSystem()
     //  isWakeKey()
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index e068b41..0d55544 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -412,41 +412,28 @@
      */
     public static class JankData {
 
-        /** @hide */
-        @IntDef(flag = true, value = {JANK_NONE,
-                DISPLAY_HAL,
-                JANK_SURFACEFLINGER_DEADLINE_MISSED,
-                JANK_SURFACEFLINGER_GPU_DEADLINE_MISSED,
-                JANK_APP_DEADLINE_MISSED,
-                PREDICTION_ERROR,
-                SURFACE_FLINGER_SCHEDULING})
+        /**
+         * Needs to be kept in sync with android_view_SurfaceControl.cpp's
+         * JankDataListenerWrapper::onJankDataAvailable.
+         * @hide
+         */
+        @IntDef(flag = true, value = {
+            JANK_NONE,
+            JANK_COMPOSER,
+            JANK_APPLICATION,
+            JANK_OTHER,
+        })
         @Retention(RetentionPolicy.SOURCE)
         public @interface JankType {}
 
-        // Needs to be kept in sync with frameworks/native/libs/gui/include/gui/JankInfo.h
-
         // No Jank
-        public static final int JANK_NONE = 0x0;
-
-        // Jank not related to SurfaceFlinger or the App
-        public static final int DISPLAY_HAL = 0x1;
-        // SF took too long on the CPU
-        public static final int JANK_SURFACEFLINGER_DEADLINE_MISSED = 0x2;
-        // SF took too long on the GPU
-        public static final int JANK_SURFACEFLINGER_GPU_DEADLINE_MISSED = 0x4;
-        // Either App or GPU took too long on the frame
-        public static final int JANK_APP_DEADLINE_MISSED = 0x8;
-        // Vsync predictions have drifted beyond the threshold from the actual HWVsync
-        public static final int PREDICTION_ERROR = 0x10;
-        // Latching a buffer early might cause an early present of the frame
-        public static final int SURFACE_FLINGER_SCHEDULING = 0x20;
-        // A buffer is said to be stuffed if it was expected to be presented on a vsync but was
-        // presented later because the previous buffer was presented in its expected vsync. This
-        // usually happens if there is an unexpectedly long frame causing the rest of the buffers
-        // to enter a stuffed state.
-        public static final int BUFFER_STUFFING = 0x40;
-        // Jank due to unknown reasons.
-        public static final int UNKNOWN = 0x80;
+        public static final int JANK_NONE = 0;
+        // Jank caused by the composer missing a deadline
+        public static final int JANK_COMPOSER = 1 << 0;
+        // Jank caused by the application missing the composer's deadline
+        public static final int JANK_APPLICATION = 1 << 1;
+        // Jank due to other unknown reasons
+        public static final int JANK_OTHER = 1 << 2;
 
         public JankData(long frameVsyncId, @JankType int jankType, long frameIntervalNs,
                 long scheduledAppFrameTimeNs, long actualAppFrameTimeNs) {
@@ -3936,6 +3923,52 @@
         }
 
         /**
+         * Sets the intended frame rate for the surface {@link SurfaceControl}.
+         *
+         * <p>On devices that are capable of running the display at different frame rates,
+         * the system may choose a display refresh rate to better match this surface's frame
+         * rate. Usage of this API won't introduce frame rate throttling, or affect other
+         * aspects of the application's frame production pipeline. However, because the system
+         * may change the display refresh rate, calls to this function may result in changes
+         * to {@link Choreographer} callback timings, and changes to the time interval at which the
+         * system releases buffers back to the application.</p>
+         *
+         * <p>Note that this only has an effect for surfaces presented on the display. If this
+         * surface is consumed by something other than the system compositor, e.g. a media
+         * codec, this call has no effect.</p>
+         *
+         * @param sc The SurfaceControl to specify the frame rate of.
+         * @param frameRateParams The parameters describing the intended frame rate of this surface.
+         *         Refer to {@link Surface.FrameRateParams} for details.
+         * @return This transaction object.
+         *
+         * @see #clearFrameRate(SurfaceControl)
+         */
+        @NonNull
+        @FlaggedApi(com.android.graphics.surfaceflinger.flags.Flags
+                        .FLAG_ARR_SURFACECONTROL_SETFRAMERATE_API)
+        public Transaction setFrameRate(@NonNull SurfaceControl sc,
+                @NonNull Surface.FrameRateParams frameRateParams) {
+            checkPreconditions(sc);
+
+            if (com.android.graphics.surfaceflinger.flags.Flags.arrSetframerateApi()) {
+                // TODO(b/362798998): Logic currently incomplete: it uses fixed source rate over the
+                // desired min/max rates. Fix when plumbing is upgraded.
+                int compatibility = frameRateParams.getFixedSourceRate() == 0
+                        ? Surface.FRAME_RATE_COMPATIBILITY_DEFAULT
+                        : Surface.FRAME_RATE_COMPATIBILITY_FIXED_SOURCE;
+                float frameRate = compatibility == Surface.FRAME_RATE_COMPATIBILITY_DEFAULT
+                        ? frameRateParams.getDesiredMinRate()
+                        : frameRateParams.getFixedSourceRate();
+                nativeSetFrameRate(mNativeObject, sc.mNativeObject, frameRate, compatibility,
+                        frameRateParams.getChangeFrameRateStrategy());
+            } else {
+                Log.w(TAG, "setFrameRate was called but flag arr_setframerate_api is disabled");
+            }
+            return this;
+        }
+
+        /**
          * Clears the frame rate which was set for the surface {@link SurfaceControl}.
          *
          * <p>This is equivalent to calling {@link #setFrameRate(SurfaceControl, float, int, int)}
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index d9092ee..d5fc262 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -40,6 +40,7 @@
 import static android.view.flags.Flags.FLAG_SENSITIVE_CONTENT_APP_PROTECTION_API;
 import static android.view.flags.Flags.FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY;
 import static android.view.flags.Flags.FLAG_VIEW_VELOCITY_API;
+import static android.view.flags.Flags.calculateBoundsInParentFromBoundsInScreen;
 import static android.view.flags.Flags.enableUseMeasureCacheDuringForceLayout;
 import static android.view.flags.Flags.sensitiveContentAppProtection;
 import static android.view.flags.Flags.toolkitFrameRateAnimationBugfix25q1;
@@ -967,6 +968,13 @@
     private static boolean sAlwaysRemeasureExactly = false;
 
     /**
+     * When true calculates the bounds in parent from bounds in screen relative to its parents.
+     * This addresses the deprecated API (setBoundsInParent) in Compose, which causes empty
+     * getBoundsInParent call for Compose apps.
+     */
+    private static boolean sCalculateBoundsInParentFromBoundsInScreenFlagValue = false;
+
+    /**
      * When true makes it possible to use onMeasure caches also when the force layout flag is
      * enabled. This helps avoiding multiple measures in the same frame with the same dimensions.
      */
@@ -2556,6 +2564,8 @@
 
         sToolkitSetFrameRateReadOnlyFlagValue = toolkitSetFrameRateReadOnly();
         sToolkitMetricsForFrameRateDecisionFlagValue = toolkitMetricsForFrameRateDecision();
+        sCalculateBoundsInParentFromBoundsInScreenFlagValue =
+                calculateBoundsInParentFromBoundsInScreen();
         sUseMeasureCacheDuringForceLayoutFlagValue = enableUseMeasureCacheDuringForceLayout();
     }
 
@@ -9776,7 +9786,7 @@
             structure.setChildCount(1);
             final ViewStructure root = structure.newChild(0);
             if (info != null) {
-                populateVirtualStructure(root, provider, info, forAutofill);
+                populateVirtualStructure(root, provider, info, null, forAutofill);
                 info.recycle();
             } else {
                 Log.w(AUTOFILL_LOG_TAG, "AccessibilityNodeInfo is null.");
@@ -11075,11 +11085,19 @@
 
     private void populateVirtualStructure(ViewStructure structure,
             AccessibilityNodeProvider provider, AccessibilityNodeInfo info,
-            boolean forAutofill) {
+            @Nullable AccessibilityNodeInfo parentInfo, boolean forAutofill) {
         structure.setId(AccessibilityNodeInfo.getVirtualDescendantId(info.getSourceNodeId()),
                 null, null, info.getViewIdResourceName());
         Rect rect = structure.getTempRect();
-        info.getBoundsInParent(rect);
+        // The bounds in parent for Jetpack Compose views aren't set as setBoundsInParent is
+        // deprecated, and only setBoundsInScreen is called.
+        // The bounds in parent can be calculated by diff'ing the child view's bounds in screen with
+        // the parent's.
+        if (sCalculateBoundsInParentFromBoundsInScreenFlagValue) {
+            getBoundsInParent(info, parentInfo, rect);
+        } else {
+            info.getBoundsInParent(rect);
+        }
         structure.setDimens(rect.left, rect.top, 0, 0, rect.width(), rect.height());
         structure.setVisibility(VISIBLE);
         structure.setEnabled(info.isEnabled());
@@ -11163,13 +11181,32 @@
                         AccessibilityNodeInfo.getVirtualDescendantId(info.getChildId(i)));
                 if (cinfo != null) {
                     ViewStructure child = structure.newChild(i);
-                    populateVirtualStructure(child, provider, cinfo, forAutofill);
+                    populateVirtualStructure(child, provider, cinfo, info, forAutofill);
                     cinfo.recycle();
                 }
             }
         }
     }
 
+    private void getBoundsInParent(@NonNull AccessibilityNodeInfo info,
+            @Nullable AccessibilityNodeInfo parentInfo, @NonNull Rect rect) {
+        info.getBoundsInParent(rect);
+        // Fallback to calculate bounds in parent by diffing the bounds in
+        // screen if it's all 0.
+        if ((rect.left | rect.top | rect.right | rect.bottom) == 0) {
+            if (parentInfo != null) {
+                Rect parentBoundsInScreen = parentInfo.getBoundsInScreen();
+                Rect boundsInScreen = info.getBoundsInScreen();
+                rect.set(boundsInScreen.left - parentBoundsInScreen.left,
+                        boundsInScreen.top - parentBoundsInScreen.top,
+                        boundsInScreen.right - parentBoundsInScreen.left,
+                        boundsInScreen.bottom - parentBoundsInScreen.top);
+            } else {
+                info.getBoundsInScreen(rect);
+            }
+        }
+    }
+
     /**
      * Dispatch creation of {@link ViewStructure} down the hierarchy.  The default
      * implementation calls {@link #onProvideStructure} and
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index d57a880..0a2a2dc 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -6544,7 +6544,7 @@
         }
 
         Configuration globalConfig = mergedConfiguration.getGlobalConfiguration();
-        final Configuration overrideConfig = mergedConfiguration.getOverrideConfiguration();
+        Configuration overrideConfig = mergedConfiguration.getOverrideConfiguration();
         if (DEBUG_CONFIGURATION) Log.v(mTag,
                 "Applying new config to window " + mWindowAttributes.getTitle()
                         + ", globalConfig: " + globalConfig
@@ -6553,7 +6553,9 @@
         final CompatibilityInfo ci = mDisplay.getDisplayAdjustments().getCompatibilityInfo();
         if (!ci.equals(CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO)) {
             globalConfig = new Configuration(globalConfig);
+            overrideConfig = new Configuration(overrideConfig);
             ci.applyToConfiguration(mNoncompatDensity, globalConfig);
+            ci.applyToConfiguration(mNoncompatDensity, overrideConfig);
         }
 
         synchronized (sConfigCallbacks) {
@@ -9323,7 +9325,7 @@
             boolean insetsPending) throws RemoteException {
         final WindowConfiguration winConfigFromAm = getConfiguration().windowConfiguration;
         final WindowConfiguration winConfigFromWm =
-                mLastReportedMergedConfiguration.getGlobalConfiguration().windowConfiguration;
+                mLastReportedMergedConfiguration.getMergedConfiguration().windowConfiguration;
         final WindowConfiguration winConfig = getCompatWindowConfiguration();
         final int measuredWidth = mMeasuredWidth;
         final int measuredHeight = mMeasuredHeight;
diff --git a/core/java/android/view/accessibility/AccessibilityEvent.java b/core/java/android/view/accessibility/AccessibilityEvent.java
index dfac244..4e9d054 100644
--- a/core/java/android/view/accessibility/AccessibilityEvent.java
+++ b/core/java/android/view/accessibility/AccessibilityEvent.java
@@ -798,6 +798,18 @@
     @FlaggedApi(Flags.FLAG_TRI_STATE_CHECKED)
     public static final int CONTENT_CHANGE_TYPE_CHECKED = 1 << 13;
 
+    /**
+     * Change type for {@link #TYPE_WINDOW_CONTENT_CHANGED} event: The source node changed its
+     * expanded state which is returned by {@link AccessibilityNodeInfo#getExpandedState()}. The
+     * view changing the node's expanded state should call {@link
+     * AccessibilityNodeInfo#setExpandedState(int)} and then send this event.
+     *
+     * @see AccessibilityNodeInfo#getExpandedState()
+     * @see AccessibilityNodeInfo#setExpandedState(int)
+     */
+    @FlaggedApi(Flags.FLAG_A11Y_EXPANSION_STATE_API)
+    public static final int CONTENT_CHANGE_TYPE_EXPANDED = 1 << 14;
+
     // Speech state change types.
 
     /** Change type for {@link #TYPE_SPEECH_STATE_CHANGE} event: another service is speaking. */
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index 60ccb77..f70f7e6 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -756,6 +756,56 @@
     public static final String ACTION_ARGUMENT_SCROLL_AMOUNT_FLOAT =
             "android.view.accessibility.action.ARGUMENT_SCROLL_AMOUNT_FLOAT";
 
+    // Expanded state types.
+
+    /**
+     * Expanded state for a non-expandable element
+     *
+     * @see #getExpandedState()
+     * @see #setExpandedState(int)
+     */
+    @FlaggedApi(Flags.FLAG_A11Y_EXPANSION_STATE_API)
+    public static final int EXPANDED_STATE_UNDEFINED = 0;
+
+    /**
+     * Expanded state for a collapsed expandable element.
+     *
+     * @see #getExpandedState()
+     * @see #setExpandedState(int)
+     */
+    @FlaggedApi(Flags.FLAG_A11Y_EXPANSION_STATE_API)
+    public static final int EXPANDED_STATE_COLLAPSED = 1;
+
+    /**
+     * Expanded state for an expanded expandable element that can still be expanded further.
+     *
+     * @see #getExpandedState()
+     * @see #setExpandedState(int)
+     */
+    @FlaggedApi(Flags.FLAG_A11Y_EXPANSION_STATE_API)
+    public static final int EXPANDED_STATE_PARTIAL = 2;
+
+    /**
+     * Expanded state for a expanded expandable element that cannot be expanded further.
+     *
+     * @see #getExpandedState()
+     * @see #setExpandedState(int)
+     */
+    @FlaggedApi(Flags.FLAG_A11Y_EXPANSION_STATE_API)
+    public static final int EXPANDED_STATE_FULL = 3;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(
+            prefix = "EXPANDED_STATE_",
+            value = {
+                EXPANDED_STATE_UNDEFINED,
+                EXPANDED_STATE_COLLAPSED,
+                EXPANDED_STATE_PARTIAL,
+                EXPANDED_STATE_FULL,
+            })
+    public @interface ExpandedState {}
+
     // Focus types.
 
     /**
@@ -806,7 +856,7 @@
      * inside the CharSequence returned by {@link #getText()}, and the length must be positive.
      * <p>
      * The data can be retrieved from the {@code Bundle} returned by {@link #getExtras()} using this
-     * string as a key for {@link Bundle#getParcelableArray(String)}. The
+     * string as a key for {@link Bundle#getParcelableArray(String, Class)}. The
      * {@link android.graphics.RectF} will be null for characters that either do not exist or are
      * off the screen.
      *
@@ -1048,6 +1098,10 @@
     private int mMaxTextLength = -1;
     private int mMovementGranularities;
 
+    // TODO(b/362782158) Initialize mExpandedState explicitly with
+    // the EXPANDED_STATE_UNDEFINED state when flagging is removed.
+    private int mExpandedState;
+
     private int mTextSelectionStart = UNDEFINED_SELECTION_INDEX;
     private int mTextSelectionEnd = UNDEFINED_SELECTION_INDEX;
     private int mInputType = InputType.TYPE_NULL;
@@ -1931,6 +1985,47 @@
     }
 
     /**
+     * Sets the expanded state of the node.
+     *
+     * <p><strong>Note:</strong> Cannot be called from an {@link
+     * android.accessibilityservice.AccessibilityService}. This class is made immutable before being
+     * delivered to an {@link android.accessibilityservice.AccessibilityService}.
+     *
+     * @param state new expanded state of this node.
+     * @throws IllegalArgumentException If state is not one of:
+     *     <ul>
+     *       <li>{@link #EXPANDED_STATE_UNDEFINED}
+     *       <li>{@link #EXPANDED_STATE_COLLAPSED}
+     *       <li>{@link #EXPANDED_STATE_PARTIAL}
+     *       <li>{@link #EXPANDED_STATE_FULL}
+     *     </ul>
+     *
+     * @throws IllegalStateException If called from an AccessibilityService
+     */
+    @FlaggedApi(Flags.FLAG_A11Y_EXPANSION_STATE_API)
+    public void setExpandedState(@ExpandedState int state) {
+        enforceValidExpandedState(state);
+        enforceNotSealed();
+        mExpandedState = state;
+    }
+
+    /**
+     * Gets the expanded state for this node.
+     *
+     * @return The expanded state, one of:
+     *     <ul>
+     *       <li>{@link #EXPANDED_STATE_UNDEFINED}
+     *       <li>{@link #EXPANDED_STATE_COLLAPSED}
+     *       <li>{@link #EXPANDED_STATE_FULL}
+     *       <li>{@link #EXPANDED_STATE_PARTIAL}
+     *     </ul>
+     */
+    @FlaggedApi(Flags.FLAG_A11Y_EXPANSION_STATE_API)
+    public @ExpandedState int getExpandedState() {
+        return mExpandedState;
+    }
+
+    /**
      * Sets the minimum time duration between two content change events, which is used in throttling
      * content change events in accessibility services.
      *
@@ -4369,6 +4464,20 @@
         }
     }
 
+    private void enforceValidExpandedState(int state) {
+        if (Flags.a11yExpansionStateApi()) {
+            switch (state) {
+                case EXPANDED_STATE_UNDEFINED:
+                case EXPANDED_STATE_COLLAPSED:
+                case EXPANDED_STATE_PARTIAL:
+                case EXPANDED_STATE_FULL:
+                    return;
+                default:
+                    throw new IllegalArgumentException("Unknown expanded state: " + state);
+            }
+        }
+    }
+
     /**
      * Enforces that this instance is not sealed.
      *
@@ -4623,6 +4732,11 @@
         if (mChecked != DEFAULT.mChecked) {
             nonDefaultFields |= bitAt(fieldIndex);
         }
+        fieldIndex++;
+        if (mExpandedState != DEFAULT.mExpandedState) {
+            nonDefaultFields |= bitAt(fieldIndex);
+        }
+
         int totalFields = fieldIndex;
         parcel.writeLong(nonDefaultFields);
 
@@ -4794,6 +4908,9 @@
         if (isBitSet(nonDefaultFields, fieldIndex++)) {
             parcel.writeInt(mChecked);
         }
+        if (isBitSet(nonDefaultFields, fieldIndex++)) {
+            parcel.writeInt(mExpandedState);
+        }
 
         if (DEBUG) {
             fieldIndex--;
@@ -4883,6 +5000,7 @@
         mLeashedParent = other.mLeashedParent;
         mLeashedParentNodeId = other.mLeashedParentNodeId;
         mChecked = other.mChecked;
+        mExpandedState = other.mExpandedState;
     }
 
     private void initCopyInfos(AccessibilityNodeInfo other) {
@@ -5075,6 +5193,9 @@
         if (isBitSet(nonDefaultFields, fieldIndex++)) {
             mChecked = parcel.readInt();
         }
+        if (isBitSet(nonDefaultFields, fieldIndex++)) {
+            mExpandedState = parcel.readInt();
+        }
 
         mSealed = sealed;
     }
@@ -5249,6 +5370,26 @@
         }
     }
 
+    private static String getExpandedStateSymbolicName(int state) {
+        if (Flags.a11yExpansionStateApi()) {
+            switch (state) {
+                case EXPANDED_STATE_UNDEFINED:
+                    return "EXPANDED_STATE_UNDEFINED";
+                case EXPANDED_STATE_COLLAPSED:
+                    return "EXPANDED_STATE_COLLAPSED";
+                case EXPANDED_STATE_PARTIAL:
+                    return "EXPANDED_STATE_PARTIAL";
+                case EXPANDED_STATE_FULL:
+                    return "EXPANDED_STATE_FULL";
+                default:
+                    throw new IllegalArgumentException("Unknown expanded state: " + state);
+            }
+        } else {
+            // TODO(b/362782158) Remove when flag is removed.
+            return "";
+        }
+    }
+
     private static boolean canPerformRequestOverConnection(int connectionId,
             int windowId, long accessibilityNodeId) {
         final boolean hasWindowId = windowId != AccessibilityWindowInfo.UNDEFINED_WINDOW_ID;
@@ -5346,6 +5487,7 @@
         builder.append("; containerTitle: ").append(mContainerTitle);
         builder.append("; viewIdResName: ").append(mViewIdResourceName);
         builder.append("; uniqueId: ").append(mUniqueId);
+        builder.append("; expandedState: ").append(getExpandedStateSymbolicName(mExpandedState));
 
         builder.append("; checkable: ").append(isCheckable());
         builder.append("; checked: ").append(isChecked());
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 2d2f4c9..7dc77b1 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -2400,10 +2400,14 @@
             if (Flags.refactorInsetsController()) {
                 final var viewRootImpl = view.getViewRootImpl();
                 // In case of a running show IME animation, it should not be requested visible,
-                // otherwise the animation would jump and not be controlled by the user anymore
-                if (viewRootImpl != null
-                        && (viewRootImpl.getInsetsController().computeUserAnimatingTypes()
-                                & WindowInsets.Type.ime()) == 0) {
+                // otherwise the animation would jump and not be controlled by the user anymore.
+                // If predictive back is in progress, and a editText is focussed, we should
+                // show the IME.
+                if (viewRootImpl != null && (
+                        (viewRootImpl.getInsetsController().computeUserAnimatingTypes()
+                                & WindowInsets.Type.ime()) == 0
+                        || viewRootImpl.getInsetsController()
+                                .isPredictiveBackImeHideAnimInProgress())) {
                     ImeTracker.forLogging().onProgress(statsToken,
                             ImeTracker.PHASE_CLIENT_NO_ONGOING_USER_ANIMATION);
                     if (resultReceiver != null) {
diff --git a/core/java/android/webkit/WebViewUpdateService.java b/core/java/android/webkit/WebViewUpdateService.java
index 644d917..a26ba3f 100644
--- a/core/java/android/webkit/WebViewUpdateService.java
+++ b/core/java/android/webkit/WebViewUpdateService.java
@@ -16,14 +16,18 @@
 
 package android.webkit;
 
+import android.annotation.FlaggedApi;
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.os.RemoteException;
 
 /**
+ * @deprecated Use the {@link WebViewUpdateManager} class instead.
  * @hide
  */
+@FlaggedApi(Flags.FLAG_UPDATE_SERVICE_IPC_WRAPPER)
+@Deprecated
 @SystemApi
 public final class WebViewUpdateService {
 
diff --git a/core/java/android/window/BackEvent.java b/core/java/android/window/BackEvent.java
index 1b9235b..89b38d8 100644
--- a/core/java/android/window/BackEvent.java
+++ b/core/java/android/window/BackEvent.java
@@ -23,7 +23,6 @@
 import android.annotation.FlaggedApi;
 import android.annotation.FloatRange;
 import android.annotation.IntDef;
-import android.util.TimeUtils;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -76,7 +75,7 @@
     }
 
     /**
-     * Creates a new {@link BackEvent} instance with the current uptime as frame time.
+     * Creates a new {@link BackEvent} instance with a frame time of 0.
      *
      * @param touchX Absolute X location of the touch point of this event.
      * @param touchY Absolute Y location of the touch point of this event.
@@ -88,7 +87,7 @@
         mTouchY = touchY;
         mProgress = progress;
         mSwipeEdge = swipeEdge;
-        mFrameTimeMillis = System.nanoTime() / TimeUtils.NANOS_PER_MS;
+        mFrameTimeMillis = 0;
     }
 
     /**
diff --git a/core/java/android/window/DesktopModeFlags.java b/core/java/android/window/DesktopModeFlags.java
index 05dc910..22eec29 100644
--- a/core/java/android/window/DesktopModeFlags.java
+++ b/core/java/android/window/DesktopModeFlags.java
@@ -49,6 +49,7 @@
     ENABLE_CAPTION_COMPAT_INSET_FORCE_CONSUMPTION_ALWAYS(
             Flags::enableCaptionCompatInsetForceConsumptionAlways, true),
     ENABLE_CASCADING_WINDOWS(Flags::enableCascadingWindows, true),
+    ENABLE_TILE_RESIZING(Flags::enableTileResizing, true),
     ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY(
             Flags::enableDesktopWindowingWallpaperActivity, true),
     ENABLE_DESKTOP_WINDOWING_MODALS_POLICY(Flags::enableDesktopWindowingModalsPolicy, true),
diff --git a/core/java/android/window/flags/lse_desktop_experience.aconfig b/core/java/android/window/flags/lse_desktop_experience.aconfig
index b805f5a..731d100 100644
--- a/core/java/android/window/flags/lse_desktop_experience.aconfig
+++ b/core/java/android/window/flags/lse_desktop_experience.aconfig
@@ -336,6 +336,13 @@
 }
 
 flag {
+    name: "enable_desktop_app_launch_transitions"
+    namespace: "lse_desktop_experience"
+    description: "Enables custom transitions for app launches in Desktop Mode."
+    bug: "375992828"
+}
+
+flag {
     name: "enable_move_to_next_display_shortcut"
     namespace: "lse_desktop_experience"
     description: "Add new keyboard shortcut of moving a task into next display"
@@ -362,3 +369,17 @@
     description: "Change the default display's windowing mode to freeform when display connected in extended mode."
     bug: "374849026"
 }
+
+flag {
+    name: "enable_desktop_windowing_pip"
+    namespace: "lse_desktop_experience"
+    description: "Enables PiP features in desktop mode."
+    bug: "350475854"
+}
+
+flag {
+    name: "enable_desktop_windowing_hsum"
+    namespace: "lse_desktop_experience"
+    description: "Enables HSUM on desktop mode."
+    bug: "366397912"
+}
\ No newline at end of file
diff --git a/core/java/android/window/flags/windowing_frontend.aconfig b/core/java/android/window/flags/windowing_frontend.aconfig
index 966e835..ed7a796 100644
--- a/core/java/android/window/flags/windowing_frontend.aconfig
+++ b/core/java/android/window/flags/windowing_frontend.aconfig
@@ -340,3 +340,11 @@
     is_fixed_read_only: true
     bug: "362938401"
 }
+
+flag {
+  name: "unify_back_navigation_transition"
+  namespace: "windowing_frontend"
+  description: "Always create predictive back transition when start back gesture animation"
+  bug: "372230928"
+  is_fixed_read_only: true
+}
diff --git a/core/java/com/android/internal/jank/FrameTracker.java b/core/java/com/android/internal/jank/FrameTracker.java
index 003393c..0af4bea 100644
--- a/core/java/com/android/internal/jank/FrameTracker.java
+++ b/core/java/com/android/internal/jank/FrameTracker.java
@@ -16,13 +16,9 @@
 
 package com.android.internal.jank;
 
-import static android.view.SurfaceControl.JankData.DISPLAY_HAL;
-import static android.view.SurfaceControl.JankData.JANK_APP_DEADLINE_MISSED;
+import static android.view.SurfaceControl.JankData.JANK_APPLICATION;
+import static android.view.SurfaceControl.JankData.JANK_COMPOSER;
 import static android.view.SurfaceControl.JankData.JANK_NONE;
-import static android.view.SurfaceControl.JankData.JANK_SURFACEFLINGER_DEADLINE_MISSED;
-import static android.view.SurfaceControl.JankData.JANK_SURFACEFLINGER_GPU_DEADLINE_MISSED;
-import static android.view.SurfaceControl.JankData.PREDICTION_ERROR;
-import static android.view.SurfaceControl.JankData.SURFACE_FLINGER_SCHEDULING;
 
 import static com.android.internal.jank.DisplayRefreshRate.UNKNOWN_REFRESH_RATE;
 import static com.android.internal.jank.DisplayRefreshRate.VARIABLE_REFRESH_RATE;
@@ -181,23 +177,11 @@
                 case JANK_NONE:
                     str.append("JANK_NONE");
                     break;
-                case JANK_APP_DEADLINE_MISSED:
-                    str.append("JANK_APP_DEADLINE_MISSED");
+                case JANK_APPLICATION:
+                    str.append("JANK_APPLICATION");
                     break;
-                case JANK_SURFACEFLINGER_DEADLINE_MISSED:
-                    str.append("JANK_SURFACEFLINGER_DEADLINE_MISSED");
-                    break;
-                case JANK_SURFACEFLINGER_GPU_DEADLINE_MISSED:
-                    str.append("JANK_SURFACEFLINGER_GPU_DEADLINE_MISSED");
-                    break;
-                case DISPLAY_HAL:
-                    str.append("DISPLAY_HAL");
-                    break;
-                case PREDICTION_ERROR:
-                    str.append("PREDICTION_ERROR");
-                    break;
-                case SURFACE_FLINGER_SCHEDULING:
-                    str.append("SURFACE_FLINGER_SCHEDULING");
+                case JANK_COMPOSER:
+                    str.append("JANK_COMPOSER");
                     break;
                 default:
                     str.append("UNKNOWN: ").append(jankType);
@@ -628,16 +612,12 @@
             if (info.surfaceControlCallbackFired) {
                 totalFramesCount++;
                 boolean missedFrame = false;
-                if ((info.jankType & JANK_APP_DEADLINE_MISSED) != 0) {
+                if ((info.jankType & JANK_APPLICATION) != 0) {
                     Log.w(TAG, "Missed App frame:" + info + ", CUJ=" + name);
                     missedAppFramesCount++;
                     missedFrame = true;
                 }
-                if ((info.jankType & DISPLAY_HAL) != 0
-                        || (info.jankType & JANK_SURFACEFLINGER_DEADLINE_MISSED) != 0
-                        || (info.jankType & JANK_SURFACEFLINGER_GPU_DEADLINE_MISSED) != 0
-                        || (info.jankType & SURFACE_FLINGER_SCHEDULING) != 0
-                        || (info.jankType & PREDICTION_ERROR) != 0) {
+                if ((info.jankType & JANK_COMPOSER) != 0) {
                     Log.w(TAG, "Missed SF frame:" + info + ", CUJ=" + name);
                     missedSfFramesCount++;
                     missedFrame = true;
diff --git a/core/java/com/android/internal/os/ApplicationSharedMemory.java b/core/java/com/android/internal/os/ApplicationSharedMemory.java
index 84f713e..e6ea29e 100644
--- a/core/java/com/android/internal/os/ApplicationSharedMemory.java
+++ b/core/java/com/android/internal/os/ApplicationSharedMemory.java
@@ -21,6 +21,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 
 import dalvik.annotation.optimization.CriticalNative;
+import dalvik.annotation.optimization.FastNative;
 
 import libcore.io.IoUtils;
 
@@ -293,4 +294,34 @@
             throw new IllegalStateException("Not mutable");
         }
     }
+
+    /**
+     * Return true if the memory has been mapped.  This never throws.
+     */
+    public boolean isMapped() {
+        return mPtr != 0;
+    }
+
+    /**
+     * Return true if the memory is mapped and mutable.  This never throws.  Note that it returns
+     * false if the memory is not mapped.
+     */
+    public boolean isMutable() {
+        return isMapped() && mMutable;
+    }
+
+    /**
+     * Provide access to the nonce block needed by {@link PropertyInvalidatedCache}.  This method
+     * returns 0 if the shared memory is not (yet) mapped.
+     */
+    public long getSystemNonceBlock() {
+        return isMapped() ? nativeGetSystemNonceBlock(mPtr) : 0;
+    }
+
+    /**
+     * Return a pointer to the system nonce cache in the shared memory region.  The method is
+     * idempotent.
+     */
+    @FastNative
+    private static native long nativeGetSystemNonceBlock(long ptr);
 }
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index 2acda8a..e402ddf 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -249,6 +249,10 @@
         return isExperimentEnabled("profilesystemserver");
     }
 
+    private static boolean shouldProfileBootClasspath() {
+        return isExperimentEnabled("profilebootclasspath");
+    }
+
     /**
      * Performs Zygote process initialization. Loads and initializes commonly used classes.
      *
@@ -352,7 +356,7 @@
             // If we are profiling the boot image, reset the Jit counters after preloading the
             // classes. We want to preload for performance, and we can use method counters to
             // infer what clases are used after calling resetJitCounters, for profile purposes.
-            if (isExperimentEnabled("profilebootclasspath")) {
+            if (shouldProfileBootClasspath()) {
                 Trace.traceBegin(Trace.TRACE_TAG_DALVIK, "ResetJitCounters");
                 VMRuntime.resetJitCounters();
                 Trace.traceEnd(Trace.TRACE_TAG_DALVIK);
@@ -460,12 +464,28 @@
                             ? String.join(":", systemServerClasspath, standaloneSystemServerJars)
                             : systemServerClasspath;
                     prepareSystemServerProfile(systemServerPaths);
+                    try {
+                        SystemProperties.set("debug.tracing.profile_system_server", "1");
+                    } catch (RuntimeException e) {
+                        Slog.e(TAG, "Failed to set debug.tracing.profile_system_server", e);
+                    }
                 } catch (Exception e) {
                     Log.wtf(TAG, "Failed to set up system server profile", e);
                 }
             }
         }
 
+        // Zygote can't set system properties due to permission denied. We need to be in System
+        // Server to set system properties, so we do it here instead of the more natural place in
+        // preloadClasses.
+        if (shouldProfileBootClasspath()) {
+            try {
+                SystemProperties.set("debug.tracing.profile_boot_classpath", "1");
+            } catch (RuntimeException e) {
+                Slog.e(TAG, "Failed to set debug.tracing.profile_boot_classpath", e);
+            }
+        }
+
         if (parsedArgs.mInvokeWith != null) {
             String[] args = parsedArgs.mRemainingArgs;
             // If we have a non-null system server class path, we'll have to duplicate the
diff --git a/core/java/com/android/internal/os/flags.aconfig b/core/java/com/android/internal/os/flags.aconfig
index 07df248..25a9fbc 100644
--- a/core/java/com/android/internal/os/flags.aconfig
+++ b/core/java/com/android/internal/os/flags.aconfig
@@ -2,6 +2,48 @@
 container: "system"
 
 flag {
+     namespace: "ravenwood"
+     name: "ravenwood_flag_rw_1"
+     description: "Ravenwood test RW flag 1"
+     bug: "311370221"
+     metadata {
+         purpose: PURPOSE_BUGFIX
+     }
+}
+
+flag {
+     namespace: "ravenwood"
+     name: "ravenwood_flag_rw_2"
+     description: "Ravenwood test RW flag 2"
+     bug: "311370221"
+     metadata {
+         purpose: PURPOSE_BUGFIX
+     }
+}
+
+flag {
+     namespace: "ravenwood"
+     name: "ravenwood_flag_ro_1"
+     description: "Ravenwood test RO flag 1"
+     is_fixed_read_only: true
+     bug: "311370221"
+     metadata {
+         purpose: PURPOSE_BUGFIX
+     }
+}
+
+flag {
+     namespace: "ravenwood"
+     name: "ravenwood_flag_ro_2"
+     description: "Ravenwood test RO flag 2"
+     is_fixed_read_only: true
+     bug: "311370221"
+     metadata {
+         purpose: PURPOSE_BUGFIX
+     }
+}
+
+flag {
     name: "enable_apache_http_legacy_preload"
     namespace: "system_performance"
     description: "Enables zygote preload of non-BCP org.apache.http.legacy.jar library."
diff --git a/core/java/com/android/internal/protolog/TEST_MAPPING b/core/java/com/android/internal/protolog/TEST_MAPPING
index b51d19d..545ba11 100644
--- a/core/java/com/android/internal/protolog/TEST_MAPPING
+++ b/core/java/com/android/internal/protolog/TEST_MAPPING
@@ -1,5 +1,5 @@
 {
-  "postsubmit": [
+  "presubmit": [
     {
       "name": "TracingTests"
     },
diff --git a/core/java/com/android/internal/widget/NotificationProgressBar.java b/core/java/com/android/internal/widget/NotificationProgressBar.java
index d3b1f97..f2b36c3 100644
--- a/core/java/com/android/internal/widget/NotificationProgressBar.java
+++ b/core/java/com/android/internal/widget/NotificationProgressBar.java
@@ -21,6 +21,10 @@
 import android.app.Notification.ProgressStyle;
 import android.content.Context;
 import android.content.res.ColorStateList;
+import android.content.res.TypedArray;
+import android.graphics.Canvas;
+import android.graphics.Matrix;
+import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.Icon;
 import android.graphics.drawable.LayerDrawable;
@@ -52,7 +56,7 @@
  * represent Notification ProgressStyle progress, such as for ridesharing and navigation.
  */
 @RemoteViews.RemoteView
-public class NotificationProgressBar extends ProgressBar {
+public final class NotificationProgressBar extends ProgressBar {
     private static final String TAG = "NotificationProgressBar";
 
     private NotificationProgressModel mProgressModel;
@@ -61,7 +65,12 @@
     private List<Part> mProgressDrawableParts = null;
 
     @Nullable
-    private Drawable mProgressTrackerDrawable = null;
+    private Drawable mTracker = null;
+    private final int mTrackerHeight;
+    private int mTrackerWidth;
+    private int mTrackerPos;
+    private final Matrix mMatrix = new Matrix();
+    private Matrix mTrackerDrawMatrix = null;
 
     public NotificationProgressBar(Context context) {
         this(context, null);
@@ -78,28 +87,49 @@
     public NotificationProgressBar(Context context, AttributeSet attrs, int defStyleAttr,
             int defStyleRes) {
         super(context, attrs, defStyleAttr, defStyleRes);
+
+        final TypedArray a = context.obtainStyledAttributes(
+                attrs, R.styleable.NotificationProgressBar, defStyleAttr, defStyleRes);
+        saveAttributeDataForStyleable(context, R.styleable.NotificationProgressBar, attrs, a,
+                defStyleAttr,
+                defStyleRes);
+
+        // Supports setting the tracker in xml, but ProgressStyle notifications set/override it
+        // via {@code setProgressTrackerIcon}.
+        final Drawable tracker = a.getDrawable(R.styleable.NotificationProgressBar_tracker);
+        setTracker(tracker);
+
+        // If this is configured to be non-zero, will scale the tracker drawable and ensure its
+        // aspect ration is between 2:1 to 1:2.
+        mTrackerHeight = a.getDimensionPixelSize(R.styleable.NotificationProgressBar_trackerHeight,
+                0);
     }
 
     /**
      * Setter for the notification progress model.
      *
      * @see NotificationProgressModel#fromBundle
-     * @see #setProgressModelAsync
      */
-    @RemotableViewMethod(asyncImpl = "setProgressModelAsync")
+    @RemotableViewMethod
     public void setProgressModel(@Nullable Bundle bundle) {
         Preconditions.checkArgument(bundle != null,
                 "Bundle shouldn't be null");
 
         mProgressModel = NotificationProgressModel.fromBundle(bundle);
+        final boolean isIndeterminate = mProgressModel.isIndeterminate();
+        setIndeterminate(isIndeterminate);
 
-        if (mProgressModel.isIndeterminate()) {
+        if (isIndeterminate) {
             final int indeterminateColor = mProgressModel.getIndeterminateColor();
             setIndeterminateTintList(ColorStateList.valueOf(indeterminateColor));
         } else {
+            final int progress = mProgressModel.getProgress();
+            final int progressMax = mProgressModel.getProgressMax();
             mProgressDrawableParts = processAndConvertToDrawableParts(mProgressModel.getSegments(),
                     mProgressModel.getPoints(),
-                    mProgressModel.getProgress(), mProgressModel.isStyledByProgress());
+                    progress,
+                    progressMax,
+                    mProgressModel.isStyledByProgress());
 
             try {
                 final NotificationProgressDrawable drawable = getNotificationProgressDrawable();
@@ -107,6 +137,9 @@
             } catch (IllegalStateException ex) {
                 Log.e(TAG, "Can't set parts because can't get NotificationProgressDrawable", ex);
             }
+
+            setMax(progressMax);
+            setProgress(progress);
         }
     }
 
@@ -137,6 +170,13 @@
      */
     @RemotableViewMethod(asyncImpl = "setProgressTrackerIconAsync")
     public void setProgressTrackerIcon(@Nullable Icon icon) {
+        final Drawable progressTrackerDrawable;
+        if (icon != null) {
+            progressTrackerDrawable = icon.loadDrawable(getContext());
+        } else {
+            progressTrackerDrawable = null;
+        }
+        setTracker(progressTrackerDrawable);
     }
 
     /**
@@ -150,12 +190,295 @@
             progressTrackerDrawable = null;
         }
         return () -> {
-            setProgressTrackerDrawable(progressTrackerDrawable);
+            setTracker(progressTrackerDrawable);
         };
     }
 
-    private void setProgressTrackerDrawable(@Nullable  Drawable drawable) {
-        mProgressTrackerDrawable = drawable;
+    private void setTracker(@Nullable Drawable tracker) {
+        if (isIndeterminate() && tracker != null) {
+            return;
+        }
+
+        final boolean needUpdate = mTracker != null && tracker != mTracker;
+        if (needUpdate) {
+            mTracker.setCallback(null);
+        }
+
+        if (tracker != null) {
+            tracker.setCallback(this);
+            if (getMirrorForRtl()) {
+                tracker.setAutoMirrored(true);
+            }
+
+            if (canResolveLayoutDirection()) {
+                tracker.setLayoutDirection(getLayoutDirection());
+            }
+
+            // If we're updating get the new states
+            if (needUpdate && (tracker.getIntrinsicWidth() != mTracker.getIntrinsicWidth()
+                    || tracker.getIntrinsicHeight() != mTracker.getIntrinsicHeight())) {
+                requestLayout();
+            }
+        }
+
+        mTracker = tracker;
+
+        configureTrackerBounds();
+
+        invalidate();
+
+        if (needUpdate) {
+            updateTrackerAndBarPos(getWidth(), getHeight());
+            if (tracker != null && tracker.isStateful()) {
+                // Note that if the states are different this won't work.
+                // For now, let's consider that an app bug.
+                tracker.setState(getDrawableState());
+            }
+        }
+    }
+
+    private void configureTrackerBounds() {
+        // Reset the tracker draw matrix to null
+        mTrackerDrawMatrix = null;
+
+        if (mTracker == null || mTrackerHeight <= 0) {
+            return;
+        }
+
+        final int dWidth = mTracker.getIntrinsicWidth();
+        final int dHeight = mTracker.getIntrinsicHeight();
+        if (dWidth <= 0 || dHeight <= 0) {
+            return;
+        }
+        final int maxDWidth = dHeight * 2;
+        final int maxDHeight = dWidth * 2;
+
+        mTrackerDrawMatrix = mMatrix;
+        float scale;
+        float dx = 0, dy = 0;
+
+        if (dWidth > maxDWidth) {
+            scale = (float) mTrackerHeight / (float) dHeight;
+            dx = (maxDWidth * scale - dWidth * scale) * 0.5f;
+            mTrackerWidth = (int) (maxDWidth * scale);
+        } else if (dHeight > maxDHeight) {
+            scale = (float) mTrackerHeight * 0.5f / (float) dWidth;
+            dy = (maxDHeight * scale - dHeight * scale) * 0.5f;
+            mTrackerWidth = mTrackerHeight / 2;
+        } else {
+            scale = (float) mTrackerHeight / (float) dHeight;
+            mTrackerWidth = (int) (dWidth * scale);
+        }
+
+        mTrackerDrawMatrix.setScale(scale, scale);
+        mTrackerDrawMatrix.postTranslate(Math.round(dx), Math.round(dy));
+    }
+
+    @Override
+    @RemotableViewMethod
+    public synchronized void setIndeterminate(boolean indeterminate) {
+        super.setIndeterminate(indeterminate);
+
+        if (isIndeterminate()) {
+            setTracker(null);
+        }
+    }
+
+    @Override
+    protected boolean verifyDrawable(@NonNull Drawable who) {
+        return who == mTracker || super.verifyDrawable(who);
+    }
+
+    @Override
+    public void jumpDrawablesToCurrentState() {
+        super.jumpDrawablesToCurrentState();
+
+        if (mTracker != null) {
+            mTracker.jumpToCurrentState();
+        }
+    }
+
+    @Override
+    protected void drawableStateChanged() {
+        super.drawableStateChanged();
+
+        final Drawable tracker = mTracker;
+        if (tracker != null && tracker.isStateful()
+                && tracker.setState(getDrawableState())) {
+            invalidateDrawable(tracker);
+        }
+    }
+
+    @Override
+    public void drawableHotspotChanged(float x, float y) {
+        super.drawableHotspotChanged(x, y);
+
+        if (mTracker != null) {
+            mTracker.setHotspot(x, y);
+        }
+    }
+
+    @Override
+    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+        super.onSizeChanged(w, h, oldw, oldh);
+
+        updateTrackerAndBarPos(w, h);
+    }
+
+    private void updateTrackerAndBarPos(int w, int h) {
+        final int paddedHeight = h - mPaddingTop - mPaddingBottom;
+        final Drawable bar = getCurrentDrawable();
+        final Drawable tracker = mTracker;
+
+        // The max height does not incorporate padding, whereas the height
+        // parameter does.
+        final int barHeight = Math.min(getMaxHeight(), paddedHeight);
+        final int trackerHeight = tracker == null ? 0
+                : ((mTrackerHeight == 0) ? tracker.getIntrinsicHeight() : mTrackerHeight);
+
+        // Apply offset to whichever item is taller.
+        final int barOffsetY;
+        final int trackerOffsetY;
+        if (trackerHeight > barHeight) {
+            final int offsetHeight = (paddedHeight - trackerHeight) / 2;
+            barOffsetY = offsetHeight + (trackerHeight - barHeight) / 2;
+            trackerOffsetY = offsetHeight;
+        } else {
+            final int offsetHeight = (paddedHeight - barHeight) / 2;
+            barOffsetY = offsetHeight;
+            trackerOffsetY = offsetHeight + (barHeight - trackerHeight) / 2;
+        }
+
+        if (bar != null) {
+            final int barWidth = w - mPaddingRight - mPaddingLeft;
+            bar.setBounds(0, barOffsetY, barWidth, barOffsetY + barHeight);
+        }
+
+        if (tracker != null) {
+            setTrackerPos(w, tracker, getScale(), trackerOffsetY);
+        }
+    }
+
+    private float getScale() {
+        int min = getMin();
+        int max = getMax();
+        int range = max - min;
+        return range > 0 ? (getProgress() - min) / (float) range : 0;
+    }
+
+    /**
+     * Updates the tracker drawable bounds.
+     *
+     * @param w Width of the view, including padding
+     * @param tracker Drawable used for the tracker
+     * @param scale Current progress between 0 and 1
+     * @param offsetY Vertical offset for centering. If set to
+     *            {@link Integer#MIN_VALUE}, the current offset will be used.
+     */
+    private void setTrackerPos(int w, Drawable tracker, float scale, int offsetY) {
+        int available = w - mPaddingLeft - mPaddingRight;
+        final int trackerWidth = tracker.getIntrinsicWidth();
+        final int trackerHeight = tracker.getIntrinsicHeight();
+        available -= ((mTrackerHeight == 0) ? trackerWidth : mTrackerWidth);
+
+        final int trackerPos = (int) (scale * available + 0.5f);
+
+        final int top, bottom;
+        if (offsetY == Integer.MIN_VALUE) {
+            final Rect oldBounds = tracker.getBounds();
+            top = oldBounds.top;
+            bottom = oldBounds.bottom;
+        } else {
+            top = offsetY;
+            bottom = offsetY + trackerHeight;
+        }
+
+        mTrackerPos = (isLayoutRtl() && getMirrorForRtl()) ? available - trackerPos : trackerPos;
+        final int left = 0;
+        final int right = left + trackerWidth;
+
+        final Drawable background = getBackground();
+        if (background != null) {
+            final int bkgOffsetX = mPaddingLeft;
+            final int bkgOffsetY = mPaddingTop;
+            background.setHotspotBounds(left + bkgOffsetX, top + bkgOffsetY,
+                    right + bkgOffsetX, bottom + bkgOffsetY);
+        }
+
+        // Canvas will be translated, so 0,0 is where we start drawing
+        tracker.setBounds(left, top, right, bottom);
+    }
+
+    @Override
+    public void onResolveDrawables(int layoutDirection) {
+        super.onResolveDrawables(layoutDirection);
+
+        if (mTracker != null) {
+            mTracker.setLayoutDirection(layoutDirection);
+        }
+    }
+
+    @Override
+    protected synchronized void onDraw(Canvas canvas) {
+        super.onDraw(canvas);
+        drawTracker(canvas);
+    }
+
+    /**
+     * Draw the tracker.
+     */
+    private void drawTracker(Canvas canvas) {
+        if (mTracker != null) {
+            final int saveCount = canvas.save();
+            // Translate the canvas origin to tracker position to make the draw matrix and the RtL
+            // transformations work.
+            canvas.translate(mPaddingLeft + mTrackerPos, mPaddingTop);
+            canvas.clipRect(0, 0, mTrackerWidth, mTrackerHeight);
+            if (mTrackerDrawMatrix != null) {
+                canvas.concat(mTrackerDrawMatrix);
+            }
+            mTracker.draw(canvas);
+            canvas.restoreToCount(saveCount);
+        }
+    }
+
+    @Override
+    protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        Drawable d = getCurrentDrawable();
+
+        int trackerHeight = mTracker == null ? 0 : mTracker.getIntrinsicHeight();
+        int dw = 0;
+        int dh = 0;
+        if (d != null) {
+            dw = Math.max(getMinWidth(), Math.min(getMaxWidth(), d.getIntrinsicWidth()));
+            dh = Math.max(getMinHeight(), Math.min(getMaxHeight(), d.getIntrinsicHeight()));
+            dh = Math.max(trackerHeight, dh);
+        }
+        dw += mPaddingLeft + mPaddingRight;
+        dh += mPaddingTop + mPaddingBottom;
+
+        setMeasuredDimension(resolveSizeAndState(dw, widthMeasureSpec, 0),
+                resolveSizeAndState(dh, heightMeasureSpec, 0));
+    }
+
+    @Override
+    public CharSequence getAccessibilityClassName() {
+        return NotificationProgressBar.class.getName();
+    }
+
+    @Override
+    public void onRtlPropertiesChanged(int layoutDirection) {
+        super.onRtlPropertiesChanged(layoutDirection);
+
+        final Drawable tracker = mTracker;
+        if (tracker != null) {
+            setTrackerPos(getWidth(), tracker, getScale(), Integer.MIN_VALUE);
+
+            // Since we draw translated, the drawable's bounds that it signals
+            // for invalidation won't be the actual bounds we want invalidated,
+            // so just invalidate this whole view.
+            invalidate();
+        }
     }
 
     /**
@@ -167,12 +490,18 @@
             List<ProgressStyle.Segment> segments,
             List<ProgressStyle.Point> points,
             int progress,
+            int progressMax,
             boolean isStyledByProgress
     ) {
         if (segments.isEmpty()) {
             throw new IllegalArgumentException("List of segments shouldn't be empty");
         }
 
+        final int totalLength = segments.stream().mapToInt(ProgressStyle.Segment::getLength).sum();
+        if (progressMax != totalLength) {
+            throw new IllegalArgumentException("Invalid progressMax : " + progressMax);
+        }
+
         for (ProgressStyle.Segment segment : segments) {
             final int length = segment.getLength();
             if (length <= 0) {
@@ -180,14 +509,12 @@
             }
         }
 
-        final int progressMax = segments.stream().mapToInt(ProgressStyle.Segment::getLength).sum();
-
         if (progress < 0 || progress > progressMax) {
             throw new IllegalArgumentException("Invalid progress : " + progress);
         }
         for (ProgressStyle.Point point : points) {
             final int pos = point.getPosition();
-            if (pos <= 0 || pos >= progressMax) {
+            if (pos < 0 || pos > progressMax) {
                 throw new IllegalArgumentException("Invalid Point position : " + pos);
             }
         }
@@ -208,7 +535,6 @@
                 isStyledByProgress);
     }
 
-
     // Any segment with a point on it gets split by the point.
     // If isStyledByProgress is true, also split the segment with the progress value in its range.
     private static Map<Integer, ProgressStyle.Segment> splitSegmentsByPointsAndProgress(
diff --git a/core/java/com/android/internal/widget/NotificationProgressDrawable.java b/core/java/com/android/internal/widget/NotificationProgressDrawable.java
index 2be7273..30deb49 100644
--- a/core/java/com/android/internal/widget/NotificationProgressDrawable.java
+++ b/core/java/com/android/internal/widget/NotificationProgressDrawable.java
@@ -190,12 +190,12 @@
                 final float segWidth = segment.mFraction * totalWidth;
                 // Advance the start position to account for a point immediately prior.
                 final float startOffset = getSegStartOffset(prevPart, pointRadius,
-                        mState.mSegPointGap);
+                        mState.mSegPointGap, x);
                 final float start = x + startOffset;
                 // Retract the end position to account for the padding and a point immediately
                 // after.
                 final float endOffset = getSegEndOffset(nextPart, pointRadius, mState.mSegPointGap,
-                        mState.mSegSegGap);
+                        mState.mSegSegGap, x + segWidth, totalWidth);
                 final float end = x + segWidth - endOffset;
 
                 // Transparent is not allowed (and also is the default in the data), so use that
@@ -215,8 +215,17 @@
                 // width (ignoring offset and padding)
                 x += segWidth;
             } else if (part instanceof Point point) {
-                mPointRect.set((int) (x - pointRadius), (int) (centerY - pointRadius),
-                        (int) (x + pointRadius), (int) (centerY + pointRadius));
+                final float pointWidth = 2 * pointRadius;
+                float start = x - pointRadius;
+                if (start < 0) start = 0;
+                float end = start + pointWidth;
+                if (end > totalWidth) {
+                    end = totalWidth;
+                    if (totalWidth > pointWidth) start = totalWidth - pointWidth;
+                }
+                mPointRect.set((int) start, (int) (centerY - pointRadius), (int) end,
+                        (int) (centerY + pointRadius));
+
                 if (point.mIcon != null) {
                     point.mIcon.setBounds(mPointRect);
                     point.mIcon.draw(canvas);
@@ -238,14 +247,22 @@
         }
     }
 
-    private static float getSegStartOffset(Part prevPart, float pointRadius, float segPointGap) {
-        return (prevPart instanceof Point) ? pointRadius + segPointGap : 0F;
+    private static float getSegStartOffset(Part prevPart, float pointRadius, float segPointGap,
+            float startX) {
+        if (!(prevPart instanceof Point)) return 0F;
+        final float pointOffset = (startX < pointRadius) ? (pointRadius - startX) : 0;
+        return pointOffset + pointRadius + segPointGap;
     }
 
     private static float getSegEndOffset(Part nextPart, float pointRadius, float segPointGap,
-            float segSegGap) {
+            float segSegGap, float endX, float totalWidth) {
         if (nextPart == null) return 0F;
-        return (nextPart instanceof Point) ? segPointGap + pointRadius : segSegGap;
+        if (!(nextPart instanceof Point)) return segSegGap;
+
+        final float pointWidth = 2 * pointRadius;
+        final float pointOffset = (endX + pointRadius > totalWidth && totalWidth > pointWidth)
+                ? (endX + pointRadius - totalWidth) : 0;
+        return segPointGap + pointRadius + pointOffset;
     }
 
     @Override
diff --git a/core/java/com/android/internal/widget/NotificationProgressModel.java b/core/java/com/android/internal/widget/NotificationProgressModel.java
index e51ea99..e8cb37e 100644
--- a/core/java/com/android/internal/widget/NotificationProgressModel.java
+++ b/core/java/com/android/internal/widget/NotificationProgressModel.java
@@ -96,6 +96,10 @@
         return mProgress;
     }
 
+    public int getProgressMax() {
+        return mSegments.stream().mapToInt(Notification.ProgressStyle.Segment::getLength).sum();
+    }
+
     public boolean isStyledByProgress() {
         return mIsStyledByProgress;
     }
diff --git a/core/java/com/android/internal/widget/remotecompose/core/CompanionOperation.java b/core/java/com/android/internal/widget/remotecompose/core/CompanionOperation.java
index deae9a5..244bb3d 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/CompanionOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/CompanionOperation.java
@@ -17,15 +17,13 @@
 
 import java.util.List;
 
-/**
- * Interface for the companion operations
- */
+/** Interface for the companion operations */
 public interface CompanionOperation {
     /**
      * Read, create and add instance to operations
+     *
      * @param buffer data to read to create operation
      * @param operations command is to be added
      */
     void read(WireBuffer buffer, List<Operation> operations);
 }
-
diff --git a/core/java/com/android/internal/widget/remotecompose/core/CoreDocument.java b/core/java/com/android/internal/widget/remotecompose/core/CoreDocument.java
index 18be480..212df02 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/CoreDocument.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/CoreDocument.java
@@ -16,6 +16,7 @@
 package com.android.internal.widget.remotecompose.core;
 
 import com.android.internal.widget.remotecompose.core.operations.ComponentValue;
+import com.android.internal.widget.remotecompose.core.operations.IntegerExpression;
 import com.android.internal.widget.remotecompose.core.operations.NamedVariable;
 import com.android.internal.widget.remotecompose.core.operations.RootContentBehavior;
 import com.android.internal.widget.remotecompose.core.operations.Theme;
@@ -25,6 +26,8 @@
 import com.android.internal.widget.remotecompose.core.operations.layout.ComponentEnd;
 import com.android.internal.widget.remotecompose.core.operations.layout.ComponentStartOperation;
 import com.android.internal.widget.remotecompose.core.operations.layout.LayoutComponent;
+import com.android.internal.widget.remotecompose.core.operations.layout.LoopEnd;
+import com.android.internal.widget.remotecompose.core.operations.layout.LoopOperation;
 import com.android.internal.widget.remotecompose.core.operations.layout.RootLayoutComponent;
 import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.ComponentModifiers;
 import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.ModifierOperation;
@@ -33,11 +36,12 @@
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.List;
 import java.util.Set;
 
 /**
- * Represents a platform independent RemoteCompose document,
- * containing RemoteCompose operations + state
+ * Represents a platform independent RemoteCompose document, containing RemoteCompose operations +
+ * state
  */
 public class CoreDocument {
 
@@ -66,6 +70,8 @@
 
     RemoteComposeBuffer mBuffer = new RemoteComposeBuffer(mRemoteComposeState);
 
+    private final HashMap<Long, IntegerExpression> mIntegerExpressions = new HashMap<>();
+
     private int mLastId = 1; // last component id when inflating the file
 
     public String getContentDescription() {
@@ -133,21 +139,14 @@
     /**
      * Sets the way the player handles the content
      *
-     * @param scroll    set the horizontal behavior (NONE|SCROLL_HORIZONTAL|SCROLL_VERTICAL)
+     * @param scroll set the horizontal behavior (NONE|SCROLL_HORIZONTAL|SCROLL_VERTICAL)
      * @param alignment set the alignment of the content (TOP|CENTER|BOTTOM|START|END)
-     * @param sizing    set the type of sizing for the content (NONE|SIZING_LAYOUT|SIZING_SCALE)
-     * @param mode      set the mode of sizing, either LAYOUT modes or SCALE modes
-     *                  the LAYOUT modes are:
-     *                  - LAYOUT_MATCH_PARENT
-     *                  - LAYOUT_WRAP_CONTENT
-     *                  or adding an horizontal mode and a vertical mode:
-     *                  - LAYOUT_HORIZONTAL_MATCH_PARENT
-     *                  - LAYOUT_HORIZONTAL_WRAP_CONTENT
-     *                  - LAYOUT_HORIZONTAL_FIXED
-     *                  - LAYOUT_VERTICAL_MATCH_PARENT
-     *                  - LAYOUT_VERTICAL_WRAP_CONTENT
-     *                  - LAYOUT_VERTICAL_FIXED
-     *                  The LAYOUT_*_FIXED modes will use the intrinsic document size
+     * @param sizing set the type of sizing for the content (NONE|SIZING_LAYOUT|SIZING_SCALE)
+     * @param mode set the mode of sizing, either LAYOUT modes or SCALE modes the LAYOUT modes are:
+     *     - LAYOUT_MATCH_PARENT - LAYOUT_WRAP_CONTENT or adding an horizontal mode and a vertical
+     *     mode: - LAYOUT_HORIZONTAL_MATCH_PARENT - LAYOUT_HORIZONTAL_WRAP_CONTENT -
+     *     LAYOUT_HORIZONTAL_FIXED - LAYOUT_VERTICAL_MATCH_PARENT - LAYOUT_VERTICAL_WRAP_CONTENT -
+     *     LAYOUT_VERTICAL_FIXED The LAYOUT_*_FIXED modes will use the intrinsic document size
      */
     public void setRootContentBehavior(int scroll, int alignment, int sizing, int mode) {
         this.mContentScroll = scroll;
@@ -160,8 +159,8 @@
      * Given dimensions w x h of where to paint the content, returns the corresponding scale factor
      * according to the contentSizing information
      *
-     * @param w           horizontal dimension of the rendering area
-     * @param h           vertical dimension of the rendering area
+     * @param w horizontal dimension of the rendering area
+     * @param h vertical dimension of the rendering area
      * @param scaleOutput will contain the computed scale factor
      */
     public void computeScale(float w, float h, float[] scaleOutput) {
@@ -169,50 +168,47 @@
         float contentScaleY = 1f;
         if (mContentSizing == RootContentBehavior.SIZING_SCALE) {
             // we need to add canvas transforms ops here
+            float scaleX = 1f;
+            float scaleY = 1f;
+            float scale = 1f;
             switch (mContentMode) {
-                case RootContentBehavior.SCALE_INSIDE: {
-                    float scaleX = w / mWidth;
-                    float scaleY = h / mHeight;
-                    float scale = Math.min(1f, Math.min(scaleX, scaleY));
+                case RootContentBehavior.SCALE_INSIDE:
+                    scaleX = w / mWidth;
+                    scaleY = h / mHeight;
+                    scale = Math.min(1f, Math.min(scaleX, scaleY));
                     contentScaleX = scale;
                     contentScaleY = scale;
-                }
-                break;
-                case RootContentBehavior.SCALE_FIT: {
-                    float scaleX = w / mWidth;
-                    float scaleY = h / mHeight;
-                    float scale = Math.min(scaleX, scaleY);
+                    break;
+                case RootContentBehavior.SCALE_FIT:
+                    scaleX = w / mWidth;
+                    scaleY = h / mHeight;
+                    scale = Math.min(scaleX, scaleY);
                     contentScaleX = scale;
                     contentScaleY = scale;
-                }
-                break;
-                case RootContentBehavior.SCALE_FILL_WIDTH: {
-                    float scale = w / mWidth;
+                    break;
+                case RootContentBehavior.SCALE_FILL_WIDTH:
+                    scale = w / mWidth;
                     contentScaleX = scale;
                     contentScaleY = scale;
-                }
-                break;
-                case RootContentBehavior.SCALE_FILL_HEIGHT: {
-                    float scale = h / mHeight;
+                    break;
+                case RootContentBehavior.SCALE_FILL_HEIGHT:
+                    scale = h / mHeight;
                     contentScaleX = scale;
                     contentScaleY = scale;
-                }
-                break;
-                case RootContentBehavior.SCALE_CROP: {
-                    float scaleX = w / mWidth;
-                    float scaleY = h / mHeight;
-                    float scale = Math.max(scaleX, scaleY);
+                    break;
+                case RootContentBehavior.SCALE_CROP:
+                    scaleX = w / mWidth;
+                    scaleY = h / mHeight;
+                    scale = Math.max(scaleX, scaleY);
                     contentScaleX = scale;
                     contentScaleY = scale;
-                }
-                break;
-                case RootContentBehavior.SCALE_FILL_BOUNDS: {
-                    float scaleX = w / mWidth;
-                    float scaleY = h / mHeight;
+                    break;
+                case RootContentBehavior.SCALE_FILL_BOUNDS:
+                    scaleX = w / mWidth;
+                    scaleY = h / mHeight;
                     contentScaleX = scaleX;
                     contentScaleY = scaleY;
-                }
-                break;
+                    break;
                 default:
                     // nothing
             }
@@ -225,14 +221,14 @@
      * Given dimensions w x h of where to paint the content, returns the corresponding translation
      * according to the contentAlignment information
      *
-     * @param w               horizontal dimension of the rendering area
-     * @param h               vertical dimension of the rendering area
-     * @param contentScaleX   the horizontal scale we are going to use for the content
-     * @param contentScaleY   the vertical scale we are going to use for the content
+     * @param w horizontal dimension of the rendering area
+     * @param h vertical dimension of the rendering area
+     * @param contentScaleX the horizontal scale we are going to use for the content
+     * @param contentScaleY the vertical scale we are going to use for the content
      * @param translateOutput will contain the computed translation
      */
-    private void computeTranslate(float w, float h, float contentScaleX, float contentScaleY,
-                                  float[] translateOutput) {
+    private void computeTranslate(
+            float w, float h, float contentScaleX, float contentScaleY, float[] translateOutput) {
         int horizontalContentAlignment = mContentAlignment & 0xF0;
         int verticalContentAlignment = mContentAlignment & 0xF;
         float translateX = 0f;
@@ -241,34 +237,28 @@
         float contentHeight = mHeight * contentScaleY;
 
         switch (horizontalContentAlignment) {
-            case RootContentBehavior.ALIGNMENT_START: {
+            case RootContentBehavior.ALIGNMENT_START:
                 // nothing
-            }
-            break;
-            case RootContentBehavior.ALIGNMENT_HORIZONTAL_CENTER: {
+                break;
+            case RootContentBehavior.ALIGNMENT_HORIZONTAL_CENTER:
                 translateX = (w - contentWidth) / 2f;
-            }
-            break;
-            case RootContentBehavior.ALIGNMENT_END: {
+                break;
+            case RootContentBehavior.ALIGNMENT_END:
                 translateX = w - contentWidth;
-            }
-            break;
+                break;
             default:
                 // nothing (same as alignment_start)
         }
         switch (verticalContentAlignment) {
-            case RootContentBehavior.ALIGNMENT_TOP: {
+            case RootContentBehavior.ALIGNMENT_TOP:
                 // nothing
-            }
-            break;
-            case RootContentBehavior.ALIGNMENT_VERTICAL_CENTER: {
+                break;
+            case RootContentBehavior.ALIGNMENT_VERTICAL_CENTER:
                 translateY = (h - contentHeight) / 2f;
-            }
-            break;
-            case RootContentBehavior.ALIGNMENT_BOTTOM: {
+                break;
+            case RootContentBehavior.ALIGNMENT_BOTTOM:
                 translateY = h - contentHeight;
-            }
-            break;
+                break;
             default:
                 // nothing (same as alignment_top)
         }
@@ -279,6 +269,7 @@
 
     /**
      * Returns the list of click areas
+     *
      * @return list of click areas in document coordinates
      */
     public Set<ClickAreaRepresentation> getClickAreas() {
@@ -287,15 +278,14 @@
 
     /**
      * Returns the root layout component
+     *
      * @return returns the root component if it exists, null otherwise
      */
     public RootLayoutComponent getRootLayoutComponent() {
         return mRootLayoutComponent;
     }
 
-    /**
-     * Invalidate the document for layout measures. This will trigger a layout remeasure pass.
-     */
+    /** Invalidate the document for layout measures. This will trigger a layout remeasure pass. */
     public void invalidateMeasure() {
         if (mRootLayoutComponent != null) {
             mRootLayoutComponent.invalidateMeasure();
@@ -304,6 +294,7 @@
 
     /**
      * Returns the component with the given id
+     *
      * @param id component id
      * @return the component if it exists, null otherwise
      */
@@ -332,8 +323,21 @@
     }
 
     /**
-     * Callback interface for host actions
+     * Execute an integer expression with the given id and put its value on the targetId
+     *
+     * @param expressionId the id of the integer expression
+     * @param targetId the id of the value to update with the expression
+     * @param context the current context
      */
+    public void evaluateIntExpression(long expressionId, int targetId, RemoteContext context) {
+        IntegerExpression expression = mIntegerExpressions.get(expressionId);
+        if (expression != null) {
+            int v = expression.evaluate(context);
+            context.overrideInteger(targetId, v);
+        }
+    }
+
+    /** Callback interface for host actions */
     public interface ActionCallback {
         // TODO: add payload support
         void onAction(String name);
@@ -343,6 +347,7 @@
 
     /**
      * Warn action listeners for the given named action
+     *
      * @param name the action name
      */
     public void runNamedAction(String name) {
@@ -360,9 +365,7 @@
         mActionListeners.add(callback);
     }
 
-    /**
-     * Clear existing callbacks for named host actions
-     */
+    /** Clear existing callbacks for named host actions */
     public void clearActionCallbacks() {
         mActionListeners.clear();
     }
@@ -395,13 +398,14 @@
         float mBottom;
         String mMetadata;
 
-        public ClickAreaRepresentation(int id,
-                                       String contentDescription,
-                                       float left,
-                                       float top,
-                                       float right,
-                                       float bottom,
-                                       String metadata) {
+        public ClickAreaRepresentation(
+                int id,
+                String contentDescription,
+                float left,
+                float top,
+                float right,
+                float bottom,
+                String metadata) {
             this.mId = id;
             this.mContentDescription = contentDescription;
             this.mLeft = left;
@@ -419,8 +423,7 @@
          * @return x, y coordinate is within bounds
          */
         public boolean contains(float x, float y) {
-            return x >= mLeft && x < mRight
-                    && y >= mTop && y < mBottom;
+            return x >= mLeft && x < mRight && y >= mTop && y < mBottom;
         }
 
         public float getLeft() {
@@ -452,12 +455,16 @@
         }
     }
 
-    /**
-     * Load operations from the given buffer
-     */
+    /** Load operations from the given buffer */
     public void initFromBuffer(RemoteComposeBuffer buffer) {
         mOperations = new ArrayList<Operation>();
         buffer.inflateFromBuffer(mOperations);
+        for (Operation op : mOperations) {
+            if (op instanceof IntegerExpression) {
+                IntegerExpression expression = (IntegerExpression) op;
+                mIntegerExpressions.put((long) expression.mId, expression);
+            }
+        }
         mOperations = inflateComponents(mOperations);
         mBuffer = buffer;
         for (Operation op : mOperations) {
@@ -473,6 +480,7 @@
 
     /**
      * Inflate a component tree
+     *
      * @param operations flat list of operations
      * @return nested list of operations / components
      */
@@ -482,6 +490,7 @@
         ArrayList<Operation> finalOperationsList = new ArrayList<>();
         ArrayList<Operation> ops = finalOperationsList;
         ClickModifierOperation currentClickModifier = null;
+        LoopOperation currentLoop = null;
 
         mLastId = -1;
         for (Operation o : operations) {
@@ -509,11 +518,22 @@
             } else if (o instanceof ClickModifierOperation) {
                 // TODO: refactor to add container <- component...
                 currentClickModifier = (ClickModifierOperation) o;
-                ops = ((ClickModifierOperation) o).getList();
+                ops = currentClickModifier.getList();
             } else if (o instanceof ClickModifierEnd) {
                 ops = currentComponent.getList();
                 ops.add(currentClickModifier);
                 currentClickModifier = null;
+            } else if (o instanceof LoopOperation) {
+                currentLoop = (LoopOperation) o;
+                ops = currentLoop.getList();
+            } else if (o instanceof LoopEnd) {
+                if (currentComponent != null) {
+                    ops = currentComponent.getList();
+                    ops.add(currentLoop);
+                } else {
+                    ops = finalOperationsList;
+                }
+                currentLoop = null;
             } else {
                 ops.add(o);
             }
@@ -555,8 +575,8 @@
     }
 
     /**
-     * Called when an initialization is needed, allowing the document to eg load
-     * resources / cache them.
+     * Called when an initialization is needed, allowing the document to eg load resources / cache
+     * them.
      */
     public void initializeContext(RemoteContext context) {
         mRemoteComposeState.reset();
@@ -593,7 +613,7 @@
      *
      * @param majorVersion major version number, increased upon changes breaking the compatibility
      * @param minorVersion minor version number, increased when adding new features
-     * @param patch        patch level, increased upon bugfixes
+     * @param patch patch level, increased upon bugfixes
      */
     void setVersion(int majorVersion, int minorVersion, int patch) {
         mVersion = new Version(majorVersion, minorVersion, patch);
@@ -605,22 +625,29 @@
 
     /**
      * Add a click area to the document, in root coordinates. We are not doing any specific sorting
-     * through the declared areas on click detections, which means that the first one containing
-     * the click coordinates will be the one reported; the order of addition of those click areas
-     * is therefore meaningful.
+     * through the declared areas on click detections, which means that the first one containing the
+     * click coordinates will be the one reported; the order of addition of those click areas is
+     * therefore meaningful.
      *
-     * @param id                 the id of the area, which will be reported on click
+     * @param id the id of the area, which will be reported on click
      * @param contentDescription the content description (used for accessibility)
-     * @param left               the left coordinate of the click area (in pixels)
-     * @param top                the top coordinate of the click area (in pixels)
-     * @param right              the right coordinate of the click area (in pixels)
-     * @param bottom             the bottom coordinate of the click area (in pixels)
-     * @param metadata           arbitrary metadata associated with the are, also reported on click
+     * @param left the left coordinate of the click area (in pixels)
+     * @param top the top coordinate of the click area (in pixels)
+     * @param right the right coordinate of the click area (in pixels)
+     * @param bottom the bottom coordinate of the click area (in pixels)
+     * @param metadata arbitrary metadata associated with the are, also reported on click
      */
-    public void addClickArea(int id, String contentDescription,
-                             float left, float top, float right, float bottom, String metadata) {
-        mClickAreas.add(new ClickAreaRepresentation(id,
-                contentDescription, left, top, right, bottom, metadata));
+    public void addClickArea(
+            int id,
+            String contentDescription,
+            float left,
+            float top,
+            float right,
+            float bottom,
+            String metadata) {
+        mClickAreas.add(
+                new ClickAreaRepresentation(
+                        id, contentDescription, left, top, right, bottom, metadata));
     }
 
     /**
@@ -672,9 +699,7 @@
         }
     }
 
-    /**
-     * Warn click listeners when a click area is activated
-     */
+    /** Warn click listeners when a click area is activated */
     private void warnClickListeners(ClickAreaRepresentation clickArea) {
         for (ClickCallbacks listener : mClickListeners) {
             listener.click(clickArea.mId, clickArea.mMetadata);
@@ -743,10 +768,9 @@
      * Paint the document
      *
      * @param context the provided PaintContext
-     * @param theme   the theme we want to use for this document.
+     * @param theme the theme we want to use for this document.
      */
     public void paint(RemoteContext context, int theme) {
-        long time = System.nanoTime();
         context.getPaintContext().clearNeedsRepaint();
         context.mMode = RemoteContext.ContextMode.UNSET;
 
@@ -760,8 +784,12 @@
         if (mContentSizing == RootContentBehavior.SIZING_SCALE) {
             // we need to add canvas transforms ops here
             computeScale(context.mWidth, context.mHeight, mScaleOutput);
-            computeTranslate(context.mWidth, context.mHeight,
-                    mScaleOutput[0], mScaleOutput[1], mTranslateOutput);
+            computeTranslate(
+                    context.mWidth,
+                    context.mHeight,
+                    mScaleOutput[0],
+                    mScaleOutput[1],
+                    mTranslateOutput);
             context.mPaintContext.translate(mTranslateOutput[0], mTranslateOutput[1]);
             context.mPaintContext.scale(mScaleOutput[0], mScaleOutput[1]);
         }
@@ -795,9 +823,10 @@
             // or the theme is equal as the one passed in argument to paint.
             boolean apply = true;
             if (theme != Theme.UNSPECIFIED) {
-                apply = op instanceof Theme // always apply a theme setter
-                        || context.getTheme() == theme
-                        || context.getTheme() == Theme.UNSPECIFIED;
+                apply =
+                        op instanceof Theme // always apply a theme setter
+                                || context.getTheme() == theme
+                                || context.getTheme() == Theme.UNSPECIFIED;
             }
             if (apply) {
                 op.apply(context);
@@ -808,7 +837,10 @@
             mRepaintNext = 1;
         }
         context.mMode = RemoteContext.ContextMode.UNSET;
-       // System.out.println(">>   " + (  System.nanoTime() - time)*1E-6f+" ms");
+        // System.out.println(">>   " + (  System.nanoTime() - time)*1E-6f+" ms");
+        if (DEBUG && mRootLayoutComponent != null) {
+            System.out.println(mRootLayoutComponent.displayHierarchy());
+        }
     }
 
     public String[] getStats() {
@@ -831,7 +863,6 @@
             if (mOperation instanceof Component) {
                 Component com = (Component) mOperation;
                 count += addChildren(com, map, buffer);
-
             }
         }
 
@@ -893,5 +924,8 @@
             }
         }
     }
-}
 
+    public List<Operation> getOperations() {
+        return mOperations;
+    }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/DocumentedCompanion.java b/core/java/com/android/internal/widget/remotecompose/core/DocumentedCompanion.java
index 661cf7c..8a792a4 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/DocumentedCompanion.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/DocumentedCompanion.java
@@ -17,6 +17,4 @@
 
 import com.android.internal.widget.remotecompose.core.documentation.DocumentedCompanionOperation;
 
-public interface DocumentedCompanion extends CompanionOperation , DocumentedCompanionOperation {
-
-}
+public interface DocumentedCompanion extends CompanionOperation, DocumentedCompanionOperation {}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/Operation.java b/core/java/com/android/internal/widget/remotecompose/core/Operation.java
index 4a8b3d7..9f565a2 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/Operation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/Operation.java
@@ -15,14 +15,10 @@
  */
 package com.android.internal.widget.remotecompose.core;
 
-/**
- * Base interface for RemoteCompose operations
- */
+/** Base interface for RemoteCompose operations */
 public interface Operation {
 
-    /**
-     * add the operation to the buffer
-     */
+    /** add the operation to the buffer */
     void write(WireBuffer buffer);
 
     /**
@@ -32,8 +28,6 @@
      */
     void apply(RemoteContext context);
 
-    /**
-     * Debug utility to display an operation + indentation
-     */
+    /** Debug utility to display an operation + indentation */
     String deepToString(String indent);
 }
diff --git a/core/java/com/android/internal/widget/remotecompose/core/Operations.java b/core/java/com/android/internal/widget/remotecompose/core/Operations.java
index 4a25b5e..acebe07 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/Operations.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/Operations.java
@@ -25,15 +25,18 @@
 import com.android.internal.widget.remotecompose.core.operations.DataListFloat;
 import com.android.internal.widget.remotecompose.core.operations.DataListIds;
 import com.android.internal.widget.remotecompose.core.operations.DataMapIds;
+import com.android.internal.widget.remotecompose.core.operations.DataMapLookup;
 import com.android.internal.widget.remotecompose.core.operations.DrawArc;
 import com.android.internal.widget.remotecompose.core.operations.DrawBitmap;
 import com.android.internal.widget.remotecompose.core.operations.DrawBitmapInt;
+import com.android.internal.widget.remotecompose.core.operations.DrawBitmapScaled;
 import com.android.internal.widget.remotecompose.core.operations.DrawCircle;
 import com.android.internal.widget.remotecompose.core.operations.DrawLine;
 import com.android.internal.widget.remotecompose.core.operations.DrawOval;
 import com.android.internal.widget.remotecompose.core.operations.DrawPath;
 import com.android.internal.widget.remotecompose.core.operations.DrawRect;
 import com.android.internal.widget.remotecompose.core.operations.DrawRoundRect;
+import com.android.internal.widget.remotecompose.core.operations.DrawSector;
 import com.android.internal.widget.remotecompose.core.operations.DrawText;
 import com.android.internal.widget.remotecompose.core.operations.DrawTextAnchored;
 import com.android.internal.widget.remotecompose.core.operations.DrawTextOnPath;
@@ -56,6 +59,10 @@
 import com.android.internal.widget.remotecompose.core.operations.ShaderData;
 import com.android.internal.widget.remotecompose.core.operations.TextData;
 import com.android.internal.widget.remotecompose.core.operations.TextFromFloat;
+import com.android.internal.widget.remotecompose.core.operations.TextLength;
+import com.android.internal.widget.remotecompose.core.operations.TextLookup;
+import com.android.internal.widget.remotecompose.core.operations.TextLookupInt;
+import com.android.internal.widget.remotecompose.core.operations.TextMeasure;
 import com.android.internal.widget.remotecompose.core.operations.TextMerge;
 import com.android.internal.widget.remotecompose.core.operations.Theme;
 import com.android.internal.widget.remotecompose.core.operations.layout.CanvasContent;
@@ -64,12 +71,15 @@
 import com.android.internal.widget.remotecompose.core.operations.layout.ComponentEnd;
 import com.android.internal.widget.remotecompose.core.operations.layout.ComponentStart;
 import com.android.internal.widget.remotecompose.core.operations.layout.LayoutComponentContent;
+import com.android.internal.widget.remotecompose.core.operations.layout.LoopEnd;
+import com.android.internal.widget.remotecompose.core.operations.layout.LoopOperation;
 import com.android.internal.widget.remotecompose.core.operations.layout.RootLayoutComponent;
 import com.android.internal.widget.remotecompose.core.operations.layout.animation.AnimationSpec;
 import com.android.internal.widget.remotecompose.core.operations.layout.managers.BoxLayout;
 import com.android.internal.widget.remotecompose.core.operations.layout.managers.CanvasLayout;
 import com.android.internal.widget.remotecompose.core.operations.layout.managers.ColumnLayout;
 import com.android.internal.widget.remotecompose.core.operations.layout.managers.RowLayout;
+import com.android.internal.widget.remotecompose.core.operations.layout.managers.StateLayout;
 import com.android.internal.widget.remotecompose.core.operations.layout.managers.TextLayout;
 import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.BackgroundModifierOperation;
 import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.BorderModifierOperation;
@@ -81,6 +91,7 @@
 import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.PaddingModifierOperation;
 import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.RoundedClipRectModifierOperation;
 import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.ValueIntegerChangeActionOperation;
+import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.ValueIntegerExpressionChangeActionOperation;
 import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.ValueStringChangeActionOperation;
 import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.WidthModifierOperation;
 import com.android.internal.widget.remotecompose.core.operations.utilities.IntMap;
@@ -88,9 +99,7 @@
 import com.android.internal.widget.remotecompose.core.types.IntegerConstant;
 import com.android.internal.widget.remotecompose.core.types.LongConstant;
 
-/**
- * List of operations supported in a RemoteCompose document
- */
+/** List of operations supported in a RemoteCompose document */
 public class Operations {
 
     ////////////////////////////////////////
@@ -112,7 +121,7 @@
     public static final int DATA_SHADER = 45;
     public static final int DATA_TEXT = 102;
 
-    /////////////////////////////=====================
+    ///////////////////////////// =====================
     public static final int CLIP_PATH = 38;
     public static final int CLIP_RECT = 39;
     public static final int PAINT_VALUES = 40;
@@ -121,7 +130,7 @@
     public static final int DRAW_CIRCLE = 46;
     public static final int DRAW_LINE = 47;
     public static final int DRAW_ROUND_RECT = 51;
-    public static final int DRAW_ARC = 52;
+    public static final int DRAW_SECTOR = 52;
     public static final int DRAW_TEXT_ON_PATH = 53;
     public static final int DRAW_OVAL = 56;
     public static final int DATA_PATH = 123;
@@ -149,8 +158,15 @@
     public static final int ID_LIST = 146;
     public static final int FLOAT_LIST = 147;
     public static final int DATA_LONG = 148;
+    public static final int DRAW_BITMAP_SCALED = 149;
+    public static final int TEXT_LOOKUP = 151;
+    public static final int DRAW_ARC = 152;
+    public static final int TEXT_LOOKUP_INT = 153;
+    public static final int DATA_MAP_LOOKUP = 154;
+    public static final int TEXT_MEASURE = 155;
+    public static final int TEXT_LENGTH = 156;
 
-    /////////////////////////////////////////======================
+    ///////////////////////////////////////// ======================
 
     ////////////////////////////////////////
     // Layout commands
@@ -163,10 +179,12 @@
     public static final int LAYOUT_COLUMN = 204;
     public static final int LAYOUT_CANVAS = 205;
     public static final int LAYOUT_CANVAS_CONTENT = 207;
-
     public static final int LAYOUT_TEXT = 208;
+    public static final int LAYOUT_STATE = 217;
+
     public static final int COMPONENT_START = 2;
     public static final int COMPONENT_END = 3;
+
     public static final int MODIFIER_WIDTH = 16;
     public static final int MODIFIER_HEIGHT = 67;
     public static final int MODIFIER_BACKGROUND = 55;
@@ -178,6 +196,8 @@
     public static final int MODIFIER_CLICK = 59;
 
     public static final int MODIFIER_CLICK_END = 214;
+    public static final int LOOP_START = 215;
+    public static final int LOOP_END = 216;
 
     public static final int MODIFIER_VISIBILITY = 211;
     public static final int HOST_ACTION = 209;
@@ -185,6 +205,7 @@
 
     public static final int VALUE_INTEGER_CHANGE_ACTION = 212;
     public static final int VALUE_STRING_CHANGE_ACTION = 213;
+    public static final int VALUE_INTEGER_EXPRESSION_CHANGE_ACTION = 218;
 
     public static final int ANIMATION_SPEC = 14;
 
@@ -194,7 +215,7 @@
 
     static class UniqueIntMap<T> extends IntMap<T> {
         @Override
-        public T put(int key,  T value)  {
+        public T put(int key, T value) {
             assert null == get(key) : "Opcode " + key + " already used in Operations !";
             return super.put(key, value);
         }
@@ -210,7 +231,7 @@
         map.put(ROOT_CONTENT_BEHAVIOR, RootContentBehavior::read);
         map.put(ROOT_CONTENT_DESCRIPTION, RootContentDescription::read);
 
-        map.put(DRAW_ARC, DrawArc::read);
+        map.put(DRAW_SECTOR, DrawSector::read);
         map.put(DRAW_BITMAP, DrawBitmap::read);
         map.put(DRAW_CIRCLE, DrawCircle::read);
         map.put(DRAW_LINE, DrawLine::read);
@@ -247,6 +268,12 @@
         map.put(ID_LIST, DataListIds::read);
         map.put(FLOAT_LIST, DataListFloat::read);
         map.put(DATA_LONG, LongConstant::read);
+        map.put(DRAW_BITMAP_SCALED, DrawBitmapScaled::read);
+        map.put(TEXT_LOOKUP, TextLookup::read);
+        map.put(TEXT_LOOKUP_INT, TextLookupInt::read);
+
+        map.put(LOOP_START, LoopOperation::read);
+        map.put(LOOP_END, LoopEnd::read);
 
         // Layout
 
@@ -267,6 +294,9 @@
         map.put(HOST_ACTION, HostActionOperation::read);
         map.put(HOST_NAMED_ACTION, HostNamedActionOperation::read);
         map.put(VALUE_INTEGER_CHANGE_ACTION, ValueIntegerChangeActionOperation::read);
+        map.put(
+                VALUE_INTEGER_EXPRESSION_CHANGE_ACTION,
+                ValueIntegerExpressionChangeActionOperation::read);
         map.put(VALUE_STRING_CHANGE_ACTION, ValueStringChangeActionOperation::read);
 
         map.put(LAYOUT_ROOT, RootLayoutComponent::read);
@@ -278,6 +308,12 @@
         map.put(LAYOUT_CANVAS_CONTENT, CanvasContent::read);
         map.put(LAYOUT_TEXT, TextLayout::read);
 
+        map.put(LAYOUT_STATE, StateLayout::read);
+
         map.put(COMPONENT_VALUE, ComponentValue::read);
+        map.put(DRAW_ARC, DrawArc::read);
+        map.put(DATA_MAP_LOOKUP, DataMapLookup::read);
+        map.put(TEXT_MEASURE, TextMeasure::read);
+        map.put(TEXT_LENGTH, TextLength::read);
     }
 }
diff --git a/core/java/com/android/internal/widget/remotecompose/core/PaintContext.java b/core/java/com/android/internal/widget/remotecompose/core/PaintContext.java
index 4770b12..13d6f78 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/PaintContext.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/PaintContext.java
@@ -17,15 +17,14 @@
 
 import com.android.internal.widget.remotecompose.core.operations.paint.PaintBundle;
 
-/**
- * Specify an abstract paint context used by RemoteCompose commands to draw
- */
+/** Specify an abstract paint context used by RemoteCompose commands to draw */
 public abstract class PaintContext {
     public static final int TEXT_MEASURE_MONOSPACE_WIDTH = 0x01;
     public static final int TEXT_MEASURE_FONT_HEIGHT = 0x02;
 
     protected RemoteContext mContext;
     private boolean mNeedsRepaint = false;
+
     public RemoteContext getContext() {
         return mContext;
     }
@@ -46,43 +45,43 @@
         this.mContext = context;
     }
 
-    /**
-     * convenience function to call matrixSave()
-     */
+    /** convenience function to call matrixSave() */
     public void save() {
         matrixSave();
     }
 
-    /**
-     * convenience function to call matrixRestore()
-     */
+    /** convenience function to call matrixRestore() */
     public void restore() {
         matrixRestore();
     }
 
-    /**
-     * convenience function to call matrixSave()
-     */
+    /** convenience function to call matrixSave() */
     public void saveLayer(float x, float y, float width, float height) {
         // TODO
         matrixSave();
     }
 
-    public abstract void drawBitmap(int imageId,
-                                    int srcLeft, int srcTop, int srcRight, int srcBottom,
-                                    int dstLeft, int dstTop, int dstRight, int dstBottom,
-                                    int cdId);
+    public abstract void drawBitmap(
+            int imageId,
+            int srcLeft,
+            int srcTop,
+            int srcRight,
+            int srcBottom,
+            int dstLeft,
+            int dstTop,
+            int dstRight,
+            int dstBottom,
+            int cdId);
 
     public abstract void scale(float scaleX, float scaleY);
 
     public abstract void translate(float translateX, float translateY);
 
-    public abstract void drawArc(float left,
-                                 float top,
-                                 float right,
-                                 float bottom,
-                                 float startAngle,
-                                 float sweepAngle);
+    public abstract void drawArc(
+            float left, float top, float right, float bottom, float startAngle, float sweepAngle);
+
+    public abstract void drawSector(
+            float left, float top, float right, float bottom, float startAngle, float sweepAngle);
 
     public abstract void drawBitmap(int id, float left, float top, float right, float bottom);
 
@@ -96,43 +95,29 @@
 
     public abstract void drawRect(float left, float top, float right, float bottom);
 
-    /**
-     * this caches the paint to a paint stack
-     */
-    public abstract void  savePaint();
+    /** this caches the paint to a paint stack */
+    public abstract void savePaint();
 
-    /**
-     * This restores the paint form the paint stack
-     */
-    public abstract void  restorePaint();
+    /** This restores the paint form the paint stack */
+    public abstract void restorePaint();
 
-    public abstract void drawRoundRect(float left,
-                                       float top,
-                                       float right,
-                                       float bottom,
-                                       float radiusX,
-                                       float radiusY);
+    public abstract void drawRoundRect(
+            float left, float top, float right, float bottom, float radiusX, float radiusY);
 
     public abstract void drawTextOnPath(int textId, int pathId, float hOffset, float vOffset);
 
     /**
-     * Return the dimensions (left, top, right, bottom).
-     * Relative to a drawTextRun x=0, y=0;
+     * Return the dimensions (left, top, right, bottom). Relative to a drawTextRun x=0, y=0;
      *
      * @param textId
      * @param start
-     * @param end    if end is -1 it means the whole string
-     * @param flags how to measure:
-     *              TEXT_MEASURE_MONOSPACE_WIDTH - measure as a monospace font
-     *              TEXT_MEASURE_FULL_HEIGHT - measure bounds of the given string using the
-     *                max ascend and descent of the font (not just of the measured text)
+     * @param end if end is -1 it means the whole string
+     * @param flags how to measure: TEXT_MEASURE_MONOSPACE_WIDTH - measure as a monospace font
+     *     TEXT_MEASURE_FULL_HEIGHT - measure bounds of the given string using the max ascend and
+     *     descent of the font (not just of the measured text)
      * @param bounds the bounds (left, top, right, bottom)
      */
-    public abstract void getTextBounds(int textId,
-                                       int start,
-                                       int end,
-                                       int flags,
-                                       float[]bounds);
+    public abstract void getTextBounds(int textId, int start, int end, int flags, float[] bounds);
 
     /**
      * Draw a text starting ast x,y
@@ -146,38 +131,38 @@
      * @param y
      * @param rtl
      */
-    public abstract void drawTextRun(int textId,
-                                     int start,
-                                     int end,
-                                     int contextStart,
-                                     int contextEnd,
-                                     float x,
-                                     float y,
-                                     boolean rtl);
+    public abstract void drawTextRun(
+            int textId,
+            int start,
+            int end,
+            int contextStart,
+            int contextEnd,
+            float x,
+            float y,
+            boolean rtl);
 
     /**
      * Draw an interpolation between two paths
+     *
      * @param path1Id
      * @param path2Id
-     * @param tween  0.0 = is path1 1.0 is path2
+     * @param tween 0.0 = is path1 1.0 is path2
      * @param start
      * @param stop
      */
-    public abstract void drawTweenPath(int path1Id,
-                                       int path2Id,
-                                       float tween,
-                                       float start,
-                                       float stop);
+    public abstract void drawTweenPath(
+            int path1Id, int path2Id, float tween, float start, float stop);
 
     /**
      * This applies changes to the current paint
+     *
      * @param mPaintData the list of changes
      */
     public abstract void applyPaint(PaintBundle mPaintData);
 
     /**
-     * Scale the rendering by scaleX and saleY (1.0 = no scale).
-     * Scaling is done about centerX,centerY.
+     * Scale the rendering by scaleX and saleY (1.0 = no scale). Scaling is done about
+     * centerX,centerY.
      *
      * @param scaleX
      * @param scaleY
@@ -188,6 +173,7 @@
 
     /**
      * Translate the rendering
+     *
      * @param translateX
      * @param translateY
      */
@@ -195,33 +181,30 @@
 
     /**
      * Skew the rendering
+     *
      * @param skewX
      * @param skewY
      */
     public abstract void matrixSkew(float skewX, float skewY);
 
     /**
-     * Rotate the rendering.
-     * Note rotates are cumulative.
+     * Rotate the rendering. Note rotates are cumulative.
+     *
      * @param rotate angle to rotate
      * @param pivotX x-coordinate about which to rotate
      * @param pivotY y-coordinate about which to rotate
      */
     public abstract void matrixRotate(float rotate, float pivotX, float pivotY);
 
-    /**
-     * Save the current state of the transform
-     */
+    /** Save the current state of the transform */
     public abstract void matrixSave();
 
-    /**
-     * Restore the previously saved state of the transform
-     */
+    /** Restore the previously saved state of the transform */
     public abstract void matrixRestore();
 
     /**
-     * Set the clip to a rectangle.
-     * Drawing outside the current clip region will have no effect
+     * Set the clip to a rectangle. Drawing outside the current clip region will have no effect
+     *
      * @param left
      * @param top
      * @param right
@@ -231,13 +214,15 @@
 
     /**
      * Clip based on a path.
+     *
      * @param pathId
      * @param regionOp
      */
     public abstract void clipPath(int pathId, int regionOp);
 
     /**
-     * Clip based ona  round rect
+     * Clip based ona round rect
+     *
      * @param width
      * @param height
      * @param topStart
@@ -245,13 +230,15 @@
      * @param bottomStart
      * @param bottomEnd
      */
-    public abstract void roundedClipRect(float width, float height,
-                                         float topStart, float topEnd,
-                                         float bottomStart, float bottomEnd);
+    public abstract void roundedClipRect(
+            float width,
+            float height,
+            float topStart,
+            float topEnd,
+            float bottomStart,
+            float bottomEnd);
 
-    /**
-     * Reset the paint
-     */
+    /** Reset the paint */
     public abstract void reset();
 
     /**
@@ -285,4 +272,3 @@
         mNeedsRepaint = true;
     }
 }
-
diff --git a/core/java/com/android/internal/widget/remotecompose/core/PaintOperation.java b/core/java/com/android/internal/widget/remotecompose/core/PaintOperation.java
index 4a1ccc9..9b7b50f 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/PaintOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/PaintOperation.java
@@ -16,8 +16,8 @@
 package com.android.internal.widget.remotecompose.core;
 
 /**
- * PaintOperation interface, used for operations aimed at painting
- * (while any operation _can_ paint, this make it a little more explicit)
+ * PaintOperation interface, used for operations aimed at painting (while any operation _can_ paint,
+ * this make it a little more explicit)
  */
 public abstract class PaintOperation implements Operation {
 
@@ -37,4 +37,13 @@
     }
 
     public abstract void paint(PaintContext context);
+
+    /**
+     * Will return true if the operation is similar enough to the current one, in the context of an
+     * animated transition.
+     */
+    public boolean suitableForTransition(Operation op) {
+        // by default expects the op to not be suitable
+        return false;
+    }
 }
diff --git a/core/java/com/android/internal/widget/remotecompose/core/Platform.java b/core/java/com/android/internal/widget/remotecompose/core/Platform.java
index 903dab4..6725e7e 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/Platform.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/Platform.java
@@ -15,13 +15,36 @@
  */
 package com.android.internal.widget.remotecompose.core;
 
-/**
- * Services that are needed to be provided by the platform during encoding.
- */
+/** Services that are needed to be provided by the platform during encoding. */
 public interface Platform {
     byte[] imageToByteArray(Object image);
-    int getImageWidth(Object image);
-    int getImageHeight(Object image);
-    float[] pathToFloatArray(Object image);
-}
 
+    int getImageWidth(Object image);
+
+    int getImageHeight(Object image);
+
+    float[] pathToFloatArray(Object path);
+
+    Platform None =
+            new Platform() {
+                @Override
+                public byte[] imageToByteArray(Object image) {
+                    throw new UnsupportedOperationException();
+                }
+
+                @Override
+                public int getImageWidth(Object image) {
+                    throw new UnsupportedOperationException();
+                }
+
+                @Override
+                public int getImageHeight(Object image) {
+                    throw new UnsupportedOperationException();
+                }
+
+                @Override
+                public float[] pathToFloatArray(Object path) {
+                    throw new UnsupportedOperationException();
+                }
+            };
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeBuffer.java b/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeBuffer.java
index 6b1828f..5b5adc2 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeBuffer.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeBuffer.java
@@ -25,15 +25,18 @@
 import com.android.internal.widget.remotecompose.core.operations.DataListFloat;
 import com.android.internal.widget.remotecompose.core.operations.DataListIds;
 import com.android.internal.widget.remotecompose.core.operations.DataMapIds;
+import com.android.internal.widget.remotecompose.core.operations.DataMapLookup;
 import com.android.internal.widget.remotecompose.core.operations.DrawArc;
 import com.android.internal.widget.remotecompose.core.operations.DrawBitmap;
 import com.android.internal.widget.remotecompose.core.operations.DrawBitmapInt;
+import com.android.internal.widget.remotecompose.core.operations.DrawBitmapScaled;
 import com.android.internal.widget.remotecompose.core.operations.DrawCircle;
 import com.android.internal.widget.remotecompose.core.operations.DrawLine;
 import com.android.internal.widget.remotecompose.core.operations.DrawOval;
 import com.android.internal.widget.remotecompose.core.operations.DrawPath;
 import com.android.internal.widget.remotecompose.core.operations.DrawRect;
 import com.android.internal.widget.remotecompose.core.operations.DrawRoundRect;
+import com.android.internal.widget.remotecompose.core.operations.DrawSector;
 import com.android.internal.widget.remotecompose.core.operations.DrawText;
 import com.android.internal.widget.remotecompose.core.operations.DrawTextAnchored;
 import com.android.internal.widget.remotecompose.core.operations.DrawTextOnPath;
@@ -55,6 +58,10 @@
 import com.android.internal.widget.remotecompose.core.operations.RootContentDescription;
 import com.android.internal.widget.remotecompose.core.operations.TextData;
 import com.android.internal.widget.remotecompose.core.operations.TextFromFloat;
+import com.android.internal.widget.remotecompose.core.operations.TextLength;
+import com.android.internal.widget.remotecompose.core.operations.TextLookup;
+import com.android.internal.widget.remotecompose.core.operations.TextLookupInt;
+import com.android.internal.widget.remotecompose.core.operations.TextMeasure;
 import com.android.internal.widget.remotecompose.core.operations.TextMerge;
 import com.android.internal.widget.remotecompose.core.operations.Theme;
 import com.android.internal.widget.remotecompose.core.operations.Utils;
@@ -62,11 +69,14 @@
 import com.android.internal.widget.remotecompose.core.operations.layout.ComponentEnd;
 import com.android.internal.widget.remotecompose.core.operations.layout.ComponentStart;
 import com.android.internal.widget.remotecompose.core.operations.layout.LayoutComponentContent;
+import com.android.internal.widget.remotecompose.core.operations.layout.LoopEnd;
+import com.android.internal.widget.remotecompose.core.operations.layout.LoopOperation;
 import com.android.internal.widget.remotecompose.core.operations.layout.RootLayoutComponent;
 import com.android.internal.widget.remotecompose.core.operations.layout.managers.BoxLayout;
 import com.android.internal.widget.remotecompose.core.operations.layout.managers.CanvasLayout;
 import com.android.internal.widget.remotecompose.core.operations.layout.managers.ColumnLayout;
 import com.android.internal.widget.remotecompose.core.operations.layout.managers.RowLayout;
+import com.android.internal.widget.remotecompose.core.operations.layout.managers.StateLayout;
 import com.android.internal.widget.remotecompose.core.operations.layout.managers.TextLayout;
 import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.BackgroundModifierOperation;
 import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.BorderModifierOperation;
@@ -76,7 +86,9 @@
 import com.android.internal.widget.remotecompose.core.operations.paint.PaintBundle;
 import com.android.internal.widget.remotecompose.core.operations.utilities.NanMap;
 import com.android.internal.widget.remotecompose.core.operations.utilities.easing.FloatAnimation;
+import com.android.internal.widget.remotecompose.core.types.BooleanConstant;
 import com.android.internal.widget.remotecompose.core.types.IntegerConstant;
+import com.android.internal.widget.remotecompose.core.types.LongConstant;
 
 import java.io.File;
 import java.io.FileInputStream;
@@ -86,9 +98,7 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 
-/**
- * Provides an abstract buffer to encode/decode RemoteCompose operations
- */
+/** Provides an abstract buffer to encode/decode RemoteCompose operations */
 public class RemoteComposeBuffer {
     public static final int EASING_CUBIC_STANDARD = FloatAnimation.CUBIC_STANDARD;
     public static final int EASING_CUBIC_ACCELERATE = FloatAnimation.CUBIC_ACCELERATE;
@@ -156,13 +166,13 @@
     /**
      * Insert a header
      *
-     * @param width              the width of the document in pixels
-     * @param height             the height of the document in pixels
+     * @param width the width of the document in pixels
+     * @param height the height of the document in pixels
      * @param contentDescription content description of the document
-     * @param capabilities       bitmask indicating needed capabilities (unused for now)
+     * @param capabilities bitmask indicating needed capabilities (unused for now)
      */
-    public void header(int width, int height, String contentDescription,
-                       float density, long capabilities) {
+    public void header(
+            int width, int height, String contentDescription, float density, long capabilities) {
         Header.apply(mBuffer, width, height, density, capabilities);
         int contentDescriptionId = 0;
         if (contentDescription != null) {
@@ -174,8 +184,8 @@
     /**
      * Insert a header
      *
-     * @param width              the width of the document in pixels
-     * @param height             the height of the document in pixels
+     * @param width the width of the document in pixels
+     * @param height the height of the document in pixels
      * @param contentDescription content description of the document
      */
     public void header(int width, int height, String contentDescription) {
@@ -185,23 +195,31 @@
     /**
      * Insert a bitmap
      *
-     * @param image       an opaque image that we'll add to the buffer
-     * @param imageWidth  the width of the image
+     * @param image an opaque image that we'll add to the buffer
+     * @param imageWidth the width of the image
      * @param imageHeight the height of the image
-     * @param srcLeft     left coordinate of the source area
-     * @param srcTop      top coordinate of the source area
-     * @param srcRight    right coordinate of the source area
-     * @param srcBottom   bottom coordinate of the source area
-     * @param dstLeft     left coordinate of the destination area
-     * @param dstTop      top coordinate of the destination area
-     * @param dstRight    right coordinate of the destination area
-     * @param dstBottom   bottom coordinate of the destination area
+     * @param srcLeft left coordinate of the source area
+     * @param srcTop top coordinate of the source area
+     * @param srcRight right coordinate of the source area
+     * @param srcBottom bottom coordinate of the source area
+     * @param dstLeft left coordinate of the destination area
+     * @param dstTop top coordinate of the destination area
+     * @param dstRight right coordinate of the destination area
+     * @param dstBottom bottom coordinate of the destination area
      */
-    public void drawBitmap(Object image,
-                           int imageWidth, int imageHeight,
-                           int srcLeft, int srcTop, int srcRight, int srcBottom,
-                           int dstLeft, int dstTop, int dstRight, int dstBottom,
-                           String contentDescription) {
+    public void drawBitmap(
+            Object image,
+            int imageWidth,
+            int imageHeight,
+            int srcLeft,
+            int srcTop,
+            int srcRight,
+            int srcBottom,
+            int dstLeft,
+            int dstTop,
+            int dstRight,
+            int dstBottom,
+            String contentDescription) {
         int imageId = mRemoteComposeState.dataGetId(image);
         if (imageId == -1) {
             imageId = mRemoteComposeState.cacheData(image);
@@ -213,14 +231,39 @@
             contentDescriptionId = addText(contentDescription);
         }
         DrawBitmapInt.apply(
-                mBuffer, imageId, srcLeft, srcTop, srcRight, srcBottom,
-                dstLeft, dstTop, dstRight, dstBottom, contentDescriptionId
-        );
+                mBuffer,
+                imageId,
+                srcLeft,
+                srcTop,
+                srcRight,
+                srcBottom,
+                dstLeft,
+                dstTop,
+                dstRight,
+                dstBottom,
+                contentDescriptionId);
     }
 
     /**
-     * Adds a text string data to the stream and returns its id
-     * Will be used to insert string with bitmaps etc.
+     * look up map and return the id of the object looked up
+     *
+     * @param mapId the map to access
+     * @param strId the string to lookup
+     * @return id containing the result of the lookup
+     */
+    public int mapLookup(int mapId, int strId) {
+        int hash = mapId + strId * 33;
+        int id = mRemoteComposeState.dataGetId(hash);
+        if (id == -1) {
+            id = mRemoteComposeState.cacheData(hash);
+            DataMapLookup.apply(mBuffer, id, mapId, strId);
+        }
+        return id;
+    }
+
+    /**
+     * Adds a text string data to the stream and returns its id Will be used to insert string with
+     * bitmaps etc.
      *
      * @param text the string to inject in the buffer
      */
@@ -236,13 +279,13 @@
     /**
      * Add a click area to the document
      *
-     * @param id                 the id of the click area, reported in the click listener callback
+     * @param id the id of the click area, reported in the click listener callback
      * @param contentDescription the content description of that click area (accessibility)
-     * @param left               left coordinate of the area bounds
-     * @param top                top coordinate of the area bounds
-     * @param right              right coordinate of the area bounds
-     * @param bottom             bottom coordinate of the area bounds
-     * @param metadata           associated metadata, user-provided
+     * @param left left coordinate of the area bounds
+     * @param top top coordinate of the area bounds
+     * @param right right coordinate of the area bounds
+     * @param bottom bottom coordinate of the area bounds
+     * @param metadata associated metadata, user-provided
      */
     public void addClickArea(
             int id,
@@ -251,8 +294,7 @@
             float top,
             float right,
             float bottom,
-            String metadata
-    ) {
+            String metadata) {
         int contentDescriptionId = 0;
         if (contentDescription != null) {
             contentDescriptionId = addText(contentDescription);
@@ -261,77 +303,84 @@
         if (metadata != null) {
             metadataId = addText(metadata);
         }
-        ClickArea.apply(mBuffer, id, contentDescriptionId,
-                left, top, right, bottom, metadataId);
+        ClickArea.apply(mBuffer, id, contentDescriptionId, left, top, right, bottom, metadataId);
     }
 
     /**
      * Sets the way the player handles the content
      *
-     * @param scroll    set the horizontal behavior (NONE|SCROLL_HORIZONTAL|SCROLL_VERTICAL)
+     * @param scroll set the horizontal behavior (NONE|SCROLL_HORIZONTAL|SCROLL_VERTICAL)
      * @param alignment set the alignment of the content (TOP|CENTER|BOTTOM|START|END)
-     * @param sizing    set the type of sizing for the content (NONE|SIZING_LAYOUT|SIZING_SCALE)
-     * @param mode      set the mode of sizing, either LAYOUT modes or SCALE modes
-     *                  the LAYOUT modes are:
-     *                  - LAYOUT_MATCH_PARENT
-     *                  - LAYOUT_WRAP_CONTENT
-     *                  or adding an horizontal mode and a vertical mode:
-     *                  - LAYOUT_HORIZONTAL_MATCH_PARENT
-     *                  - LAYOUT_HORIZONTAL_WRAP_CONTENT
-     *                  - LAYOUT_HORIZONTAL_FIXED
-     *                  - LAYOUT_VERTICAL_MATCH_PARENT
-     *                  - LAYOUT_VERTICAL_WRAP_CONTENT
-     *                  - LAYOUT_VERTICAL_FIXED
-     *                  The LAYOUT_*_FIXED modes will use the intrinsic document size
+     * @param sizing set the type of sizing for the content (NONE|SIZING_LAYOUT|SIZING_SCALE)
+     * @param mode set the mode of sizing, either LAYOUT modes or SCALE modes the LAYOUT modes are:
+     *     - LAYOUT_MATCH_PARENT - LAYOUT_WRAP_CONTENT or adding an horizontal mode and a vertical
+     *     mode: - LAYOUT_HORIZONTAL_MATCH_PARENT - LAYOUT_HORIZONTAL_WRAP_CONTENT -
+     *     LAYOUT_HORIZONTAL_FIXED - LAYOUT_VERTICAL_MATCH_PARENT - LAYOUT_VERTICAL_WRAP_CONTENT -
+     *     LAYOUT_VERTICAL_FIXED The LAYOUT_*_FIXED modes will use the intrinsic document size
      */
     public void setRootContentBehavior(int scroll, int alignment, int sizing, int mode) {
         RootContentBehavior.apply(mBuffer, scroll, alignment, sizing, mode);
     }
 
     /**
-     * add Drawing the specified arc, which will be scaled to fit inside the specified oval.
-     * <br>
+     * add Drawing the specified arc, which will be scaled to fit inside the specified oval. <br>
      * If the start angle is negative or >= 360, the start angle is treated as start angle modulo
-     * 360.
-     * <br>
+     * 360. <br>
      * If the sweep angle is >= 360, then the oval is drawn completely. Note that this differs
      * slightly from SkPath::arcTo, which treats the sweep angle modulo 360. If the sweep angle is
-     * negative, the sweep angle is treated as sweep angle modulo 360
-     * <br>
+     * negative, the sweep angle is treated as sweep angle modulo 360 <br>
      * The arc is drawn clockwise. An angle of 0 degrees correspond to the geometric angle of 0
-     * degrees (3 o'clock on a watch.)
-     * <br>
+     * degrees (3 o'clock on a watch.) <br>
      *
-     * @param left       left coordinate of oval used to define the shape and size of the arc
-     * @param top        top coordinate of oval used to define the shape and size of the arc
-     * @param right      right coordinate of oval used to define the shape and size of the arc
-     * @param bottom     bottom coordinate of oval used to define the shape and size of the arc
+     * @param left left coordinate of oval used to define the shape and size of the arc
+     * @param top top coordinate of oval used to define the shape and size of the arc
+     * @param right right coordinate of oval used to define the shape and size of the arc
+     * @param bottom bottom coordinate of oval used to define the shape and size of the arc
      * @param startAngle Starting angle (in degrees) where the arc begins
      * @param sweepAngle Sweep angle (in degrees) measured clockwise
      */
-    public void addDrawArc(float left,
-                           float top,
-                           float right,
-                           float bottom,
-                           float startAngle,
-                           float sweepAngle) {
+    public void addDrawArc(
+            float left, float top, float right, float bottom, float startAngle, float sweepAngle) {
         DrawArc.apply(mBuffer, left, top, right, bottom, startAngle, sweepAngle);
     }
 
     /**
-     * @param image              The bitmap to be drawn
-     * @param left               left coordinate of rectangle that the bitmap will be to fit into
-     * @param top                top coordinate of rectangle that the bitmap will be to fit into
-     * @param right              right coordinate of rectangle that the bitmap will be to fit into
-     * @param bottom             bottom coordinate of rectangle that the bitmap will be to fit into
+     * add Drawing the specified sector, which will be scaled to fit inside the specified oval. <br>
+     * If the start angle is negative or >= 360, the start angle is treated as start angle modulo
+     * 360. <br>
+     * If the sweep angle is >= 360, then the oval is drawn completely. Note that this differs
+     * slightly from SkPath::arcTo, which treats the sweep angle modulo 360. If the sweep angle is
+     * negative, the sweep angle is treated as sweep angle modulo 360 <br>
+     * The arc is drawn clockwise. An angle of 0 degrees correspond to the geometric angle of 0
+     * degrees (3 o'clock on a watch.) <br>
+     *
+     * @param left left coordinate of oval used to define the shape and size of the arc
+     * @param top top coordinate of oval used to define the shape and size of the arc
+     * @param right right coordinate of oval used to define the shape and size of the arc
+     * @param bottom bottom coordinate of oval used to define the shape and size of the arc
+     * @param startAngle Starting angle (in degrees) where the arc begins
+     * @param sweepAngle Sweep angle (in degrees) measured clockwise
+     */
+    public void addDrawSector(
+            float left, float top, float right, float bottom, float startAngle, float sweepAngle) {
+        DrawSector.apply(mBuffer, left, top, right, bottom, startAngle, sweepAngle);
+    }
+
+    /**
+     * @param image The bitmap to be drawn
+     * @param left left coordinate of rectangle that the bitmap will be to fit into
+     * @param top top coordinate of rectangle that the bitmap will be to fit into
+     * @param right right coordinate of rectangle that the bitmap will be to fit into
+     * @param bottom bottom coordinate of rectangle that the bitmap will be to fit into
      * @param contentDescription content description of the image
      */
-    public void addDrawBitmap(Object image,
-                              float left,
-                              float top,
-                              float right,
-                              float bottom,
-                              String contentDescription) {
+    public void addDrawBitmap(
+            Object image,
+            float left,
+            float top,
+            float right,
+            float bottom,
+            String contentDescription) {
         int imageId = mRemoteComposeState.dataGetId(image);
         if (imageId == -1) {
             imageId = mRemoteComposeState.cacheData(image);
@@ -345,9 +394,182 @@
         if (contentDescription != null) {
             contentDescriptionId = addText(contentDescription);
         }
-        DrawBitmap.apply(
-                mBuffer, imageId, left, top, right, bottom, contentDescriptionId
-        );
+        DrawBitmap.apply(mBuffer, imageId, left, top, right, bottom, contentDescriptionId);
+    }
+
+    /**
+     * @param imageId The Id bitmap to be drawn
+     * @param left left coordinate of rectangle that the bitmap will be to fit into
+     * @param top top coordinate of rectangle that the bitmap will be to fit into
+     * @param right right coordinate of rectangle that the bitmap will be to fit into
+     * @param bottom bottom coordinate of rectangle that the bitmap will be to fit into
+     * @param contentDescription content description of the image
+     */
+    public void addDrawBitmap(
+            int imageId,
+            float left,
+            float top,
+            float right,
+            float bottom,
+            String contentDescription) {
+        int contentDescriptionId = 0;
+        if (contentDescription != null) {
+            contentDescriptionId = addText(contentDescription);
+        }
+        DrawBitmap.apply(mBuffer, imageId, left, top, right, bottom, contentDescriptionId);
+    }
+
+    /**
+     * @param image The bitmap to be drawn
+     * @param srcLeft left coordinate in the source bitmap will be to extracted
+     * @param srcTop top coordinate in the source bitmap will be to extracted
+     * @param srcRight right coordinate in the source bitmap will be to extracted
+     * @param srcBottom bottom coordinate in the source bitmap will be to extracted
+     * @param dstLeft left coordinate of rectangle that the bitmap will be to fit into
+     * @param dstTop top coordinate of rectangle that the bitmap will be to fit into
+     * @param dstRight right coordinate of rectangle that the bitmap will be to fit into
+     * @param dstBottom bottom coordinate of rectangle that the bitmap will be to fit into
+     * @param scaleType The type of scaling to allow the image to fit.
+     * @param scaleFactor the scale factor when scale type is FIXED_SCALE (type = 7)
+     * @param contentDescription associate a string with image for accessibility
+     */
+    public void drawScaledBitmap(
+            Object image,
+            float srcLeft,
+            float srcTop,
+            float srcRight,
+            float srcBottom,
+            float dstLeft,
+            float dstTop,
+            float dstRight,
+            float dstBottom,
+            int scaleType,
+            float scaleFactor,
+            String contentDescription) {
+        int imageId = mRemoteComposeState.dataGetId(image);
+        if (imageId == -1) {
+            imageId = mRemoteComposeState.cacheData(image);
+            byte[] data = mPlatform.imageToByteArray(image);
+            int imageWidth = mPlatform.getImageWidth(image);
+            int imageHeight = mPlatform.getImageHeight(image);
+
+            BitmapData.apply(mBuffer, imageId, imageWidth, imageHeight, data);
+        }
+        int contentDescriptionId = 0;
+        if (contentDescription != null) {
+            contentDescriptionId = addText(contentDescription);
+        }
+        DrawBitmapScaled.apply(
+                mBuffer,
+                imageId,
+                srcLeft,
+                srcTop,
+                srcRight,
+                srcBottom,
+                dstLeft,
+                dstTop,
+                dstRight,
+                dstBottom,
+                scaleType,
+                scaleFactor,
+                contentDescriptionId);
+    }
+
+    /**
+     * Transmit bitmap so the you can use the id form. This is useful if
+     *
+     * @param image drawScaledBitmap
+     * @return id of the image useful with
+     */
+    public int addBitmap(Object image) {
+        int imageId = mRemoteComposeState.dataGetId(image);
+        if (imageId == -1) {
+            imageId = mRemoteComposeState.cacheData(image);
+            byte[] data = mPlatform.imageToByteArray(image);
+            int imageWidth = mPlatform.getImageWidth(image);
+            int imageHeight = mPlatform.getImageHeight(image);
+
+            BitmapData.apply(mBuffer, imageId, imageWidth, imageHeight, data);
+        }
+        return imageId;
+    }
+
+    /**
+     * Transmit bitmap so the you can use the id form. This is useful if
+     *
+     * @param image drawScaledBitmap
+     * @return id of the image useful with
+     */
+    public int addBitmap(Object image, String name) {
+        int imageId = mRemoteComposeState.dataGetId(image);
+        if (imageId == -1) {
+            imageId = mRemoteComposeState.cacheData(image);
+            byte[] data = mPlatform.imageToByteArray(image);
+            int imageWidth = mPlatform.getImageWidth(image);
+            int imageHeight = mPlatform.getImageHeight(image);
+
+            BitmapData.apply(mBuffer, imageId, imageWidth, imageHeight, data);
+            setBitmapName(imageId, name);
+        }
+
+        return imageId;
+    }
+
+    /**
+     * This defines the name of the color given the id.
+     *
+     * @param id of the Bitmap
+     * @param name Name of the color
+     */
+    public void setBitmapName(int id, String name) {
+        NamedVariable.apply(mBuffer, id, NamedVariable.IMAGE_TYPE, name);
+    }
+
+    /**
+     * @param imageId The id of the bitmap to be drawn
+     * @param srcLeft left coordinate in the source bitmap will be to extracted
+     * @param srcTop top coordinate in the source bitmap will be to extracted
+     * @param srcRight right coordinate in the source bitmap will be to extracted
+     * @param srcBottom bottom coordinate in the source bitmap will be to extracted
+     * @param dstLeft left coordinate of rectangle that the bitmap will be to fit into
+     * @param dstTop top coordinate of rectangle that the bitmap will be to fit into
+     * @param dstRight right coordinate of rectangle that the bitmap will be to fit into
+     * @param dstBottom bottom coordinate of rectangle that the bitmap will be to fit into
+     * @param scaleType The type of scaling to allow the image to fit.
+     * @param scaleFactor the scale factor when scale type is FIXED_SCALE (type = 7)
+     * @param contentDescription associate a string with image for accessibility
+     */
+    public void drawScaledBitmap(
+            int imageId,
+            float srcLeft,
+            float srcTop,
+            float srcRight,
+            float srcBottom,
+            float dstLeft,
+            float dstTop,
+            float dstRight,
+            float dstBottom,
+            int scaleType,
+            float scaleFactor,
+            String contentDescription) {
+        int contentDescriptionId = 0;
+        if (contentDescription != null) {
+            contentDescriptionId = addText(contentDescription);
+        }
+        DrawBitmapScaled.apply(
+                mBuffer,
+                imageId,
+                srcLeft,
+                srcTop,
+                srcRight,
+                srcBottom,
+                dstLeft,
+                dstTop,
+                dstRight,
+                dstBottom,
+                scaleType,
+                scaleFactor,
+                contentDescriptionId);
     }
 
     /**
@@ -356,7 +578,7 @@
      *
      * @param centerX The x-coordinate of the center of the circle to be drawn
      * @param centerY The y-coordinate of the center of the circle to be drawn
-     * @param radius  The radius of the circle to be drawn
+     * @param radius The radius of the circle to be drawn
      */
     public void addDrawCircle(float centerX, float centerY, float radius) {
         DrawCircle.apply(mBuffer, centerX, centerY, radius);
@@ -378,9 +600,9 @@
     /**
      * Draw the specified oval using the specified paint.
      *
-     * @param left   left coordinate of oval
-     * @param top    top coordinate of oval
-     * @param right  right coordinate of oval
+     * @param left left coordinate of oval
+     * @param top top coordinate of oval
+     * @param right right coordinate of oval
      * @param bottom bottom coordinate of oval
      */
     public void addDrawOval(float left, float top, float right, float bottom) {
@@ -389,9 +611,9 @@
 
     /**
      * Draw the specified path
-     * <p>
-     * Note: path objects are not immutable
-     * modifying them and calling this will not change the drawing
+     *
+     * <p>Note: path objects are not immutable modifying them and calling this will not change the
+     * drawing
      *
      * @param path The path to be drawn
      */
@@ -415,9 +637,9 @@
     /**
      * Draw the specified Rect
      *
-     * @param left   left coordinate of rectangle to be drawn
-     * @param top    top coordinate of rectangle to be drawn
-     * @param right  right coordinate of rectangle to be drawn
+     * @param left left coordinate of rectangle to be drawn
+     * @param top top coordinate of rectangle to be drawn
+     * @param right right coordinate of rectangle to be drawn
      * @param bottom bottom coordinate of rectangle to be drawn
      */
     public void addDrawRect(float left, float top, float right, float bottom) {
@@ -427,23 +649,23 @@
     /**
      * Draw the specified round-rect
      *
-     * @param left    left coordinate of rectangle to be drawn
-     * @param top     left coordinate of rectangle to be drawn
-     * @param right   left coordinate of rectangle to be drawn
-     * @param bottom  left coordinate of rectangle to be drawn
+     * @param left left coordinate of rectangle to be drawn
+     * @param top left coordinate of rectangle to be drawn
+     * @param right left coordinate of rectangle to be drawn
+     * @param bottom left coordinate of rectangle to be drawn
      * @param radiusX The x-radius of the oval used to round the corners
      * @param radiusY The y-radius of the oval used to round the corners
      */
-    public void addDrawRoundRect(float left, float top, float right, float bottom,
-                                 float radiusX, float radiusY) {
+    public void addDrawRoundRect(
+            float left, float top, float right, float bottom, float radiusX, float radiusY) {
         DrawRoundRect.apply(mBuffer, left, top, right, bottom, radiusX, radiusY);
     }
 
     /**
      * Draw the text, with origin at (x,y) along the specified path.
      *
-     * @param text    The text to be drawn
-     * @param path    The path the text should follow for its baseline
+     * @param text The text to be drawn
+     * @param path The path the text should follow for its baseline
      * @param hOffset The distance along the path to add to the text's starting position
      * @param vOffset The distance above(-) or below(+) the path to position the text
      */
@@ -457,91 +679,79 @@
     }
 
     /**
-     * Draw the text, with origin at (x,y). The origin is interpreted
-     * based on the Align setting in the paint.
+     * Draw the text, with origin at (x,y). The origin is interpreted based on the Align setting in
+     * the paint.
      *
-     * @param text         The text to be drawn
-     * @param start        The index of the first character in text to draw
-     * @param end          (end - 1) is the index of the last character in text to draw
+     * @param text The text to be drawn
+     * @param start The index of the first character in text to draw
+     * @param end (end - 1) is the index of the last character in text to draw
      * @param contextStart
      * @param contextEnd
-     * @param x            The x-coordinate of the origin of the text being drawn
-     * @param y            The y-coordinate of the baseline of the text being drawn
-     * @param rtl          Draw RTTL
+     * @param x The x-coordinate of the origin of the text being drawn
+     * @param y The y-coordinate of the baseline of the text being drawn
+     * @param rtl Draw RTTL
      */
-    public void addDrawTextRun(String text,
-                               int start,
-                               int end,
-                               int contextStart,
-                               int contextEnd,
-                               float x,
-                               float y,
-                               boolean rtl) {
+    public void addDrawTextRun(
+            String text,
+            int start,
+            int end,
+            int contextStart,
+            int contextEnd,
+            float x,
+            float y,
+            boolean rtl) {
         int textId = addText(text);
-        DrawText.apply(
-                mBuffer, textId, start, end,
-                contextStart, contextEnd, x, y, rtl);
+        DrawText.apply(mBuffer, textId, start, end, contextStart, contextEnd, x, y, rtl);
     }
 
     /**
-     * Draw the text, with origin at (x,y). The origin is interpreted
-     * based on the Align setting in the paint.
+     * Draw the text, with origin at (x,y). The origin is interpreted based on the Align setting in
+     * the paint.
      *
-     * @param textId       The text to be drawn
-     * @param start        The index of the first character in text to draw
-     * @param end          (end - 1) is the index of the last character in text to draw
+     * @param textId The text to be drawn
+     * @param start The index of the first character in text to draw
+     * @param end (end - 1) is the index of the last character in text to draw
      * @param contextStart
      * @param contextEnd
-     * @param x            The x-coordinate of the origin of the text being drawn
-     * @param y            The y-coordinate of the baseline of the text being drawn
-     * @param rtl          Draw RTTL
+     * @param x The x-coordinate of the origin of the text being drawn
+     * @param y The y-coordinate of the baseline of the text being drawn
+     * @param rtl Draw RTTL
      */
-    public void addDrawTextRun(int textId,
-                               int start,
-                               int end,
-                               int contextStart,
-                               int contextEnd,
-                               float x,
-                               float y,
-                               boolean rtl) {
-        DrawText.apply(
-                mBuffer, textId, start, end,
-                contextStart, contextEnd, x, y, rtl);
+    public void addDrawTextRun(
+            int textId,
+            int start,
+            int end,
+            int contextStart,
+            int contextEnd,
+            float x,
+            float y,
+            boolean rtl) {
+        DrawText.apply(mBuffer, textId, start, end, contextStart, contextEnd, x, y, rtl);
     }
 
     /**
-     * Draw a text on canvas at relative to position (x, y),
-     * offset panX and panY.
-     * <br>
-     * The panning factors (panX, panY)  mapped to the
-     * resulting bounding box of the text, in such a way that a
-     * panning factor of (0.0, 0.0) would center the text at (x, y)
+     * Draw a text on canvas at relative to position (x, y), offset panX and panY. <br>
+     * The panning factors (panX, panY) mapped to the resulting bounding box of the text, in such a
+     * way that a panning factor of (0.0, 0.0) would center the text at (x, y)
+     *
      * <ul>
-     * <li> Panning of -1.0, -1.0 - the text above & right of x,y.</li>
-     * <li>Panning of  1.0,  1.0 - the text is below and to the left</li>
-     * <li>Panning of  1.0,  0.0 - the test is centered & to the right of x,y</li>
+     *   <li>Panning of -1.0, -1.0 - the text above & right of x,y.
+     *   <li>Panning of 1.0, 1.0 - the text is below and to the left
+     *   <li>Panning of 1.0, 0.0 - the test is centered & to the right of x,y
      * </ul>
+     *
      * Setting panY to NaN results in y being the baseline of the text.
      *
-     * @param text  text to draw
-     * @param x     Coordinate of the Anchor
-     * @param y     Coordinate of the Anchor
-     * @param panX  justifies text -1.0=right, 0.0=center, 1.0=left
-     * @param panY  position text -1.0=above, 0.0=center, 1.0=below, Nan=baseline
+     * @param text text to draw
+     * @param x Coordinate of the Anchor
+     * @param y Coordinate of the Anchor
+     * @param panX justifies text -1.0=right, 0.0=center, 1.0=left
+     * @param panY position text -1.0=above, 0.0=center, 1.0=below, Nan=baseline
      * @param flags 1 = RTL
      */
-    public void drawTextAnchored(String text,
-                                 float x,
-                                 float y,
-                                 float panX,
-                                 float panY,
-                                 int flags) {
+    public void drawTextAnchored(String text, float x, float y, float panX, float panY, int flags) {
         int textId = addText(text);
-        DrawTextAnchored.apply(
-                mBuffer, textId,
-                x, y,
-                panX, panY,
-                flags);
+        DrawTextAnchored.apply(mBuffer, textId, x, y, panX, panY, flags);
     }
 
     /**
@@ -556,6 +766,7 @@
 
     /**
      * Merge two text (from id's) output one id
+     *
      * @param id1 left id
      * @param id2 right id
      * @return new id that merges the two text
@@ -576,77 +787,68 @@
     /**
      * Create a TextFromFloat command which creates text from a Float.
      *
-     * @param value        The value to convert
+     * @param value The value to convert
      * @param digitsBefore the digits before the decimal point
-     * @param digitsAfter  the digits after the decimal point
-     * @param flags        configure the behaviour using PAD_PRE_* and PAD_AFTER* flags
+     * @param digitsAfter the digits after the decimal point
+     * @param flags configure the behaviour using PAD_PRE_* and PAD_AFTER* flags
      * @return id of the string that can be passed to drawTextAnchored
      */
-    public int createTextFromFloat(float value, short digitsBefore,
-                                   short digitsAfter, int flags) {
-        String placeHolder = Utils.floatToString(value)
-                + "(" + digitsBefore + "," + digitsAfter + "," + flags + ")";
+    public int createTextFromFloat(float value, short digitsBefore, short digitsAfter, int flags) {
+        String placeHolder =
+                Utils.floatToString(value)
+                        + "("
+                        + digitsBefore
+                        + ","
+                        + digitsAfter
+                        + ","
+                        + flags
+                        + ")";
         int id = mRemoteComposeState.dataGetId(placeHolder);
         if (id == -1) {
             id = mRemoteComposeState.cacheData(placeHolder);
             //   TextData.apply(mBuffer, id, text);
         }
-        TextFromFloat.apply(mBuffer, id, value, digitsBefore,
-                digitsAfter, flags);
+        TextFromFloat.apply(mBuffer, id, value, digitsBefore, digitsAfter, flags);
         return id;
     }
 
     /**
-     * Draw a text on canvas at relative to position (x, y),
-     * offset panX and panY.
-     * <br>
-     * The panning factors (panX, panY)  mapped to the
-     * resulting bounding box of the text, in such a way that a
-     * panning factor of (0.0, 0.0) would center the text at (x, y)
+     * Draw a text on canvas at relative to position (x, y), offset panX and panY. <br>
+     * The panning factors (panX, panY) mapped to the resulting bounding box of the text, in such a
+     * way that a panning factor of (0.0, 0.0) would center the text at (x, y)
+     *
      * <ul>
-     * <li> Panning of -1.0, -1.0 - the text above & right of x,y.</li>
-     * <li>Panning of  1.0,  1.0 - the text is below and to the left</li>
-     * <li>Panning of  1.0,  0.0 - the test is centered & to the right of x,y</li>
+     *   <li>Panning of -1.0, -1.0 - the text above & right of x,y.
+     *   <li>Panning of 1.0, 1.0 - the text is below and to the left
+     *   <li>Panning of 1.0, 0.0 - the test is centered & to the right of x,y
      * </ul>
+     *
      * Setting panY to NaN results in y being the baseline of the text.
      *
      * @param textId text to draw
-     * @param x      Coordinate of the Anchor
-     * @param y      Coordinate of the Anchor
-     * @param panX   justifies text -1.0=right, 0.0=center, 1.0=left
-     * @param panY   position text -1.0=above, 0.0=center, 1.0=below, Nan=baseline
-     * @param flags  1 = RTL
+     * @param x Coordinate of the Anchor
+     * @param y Coordinate of the Anchor
+     * @param panX justifies text -1.0=right, 0.0=center, 1.0=left
+     * @param panY position text -1.0=above, 0.0=center, 1.0=below, Nan=baseline
+     * @param flags 1 = RTL
      */
-    public void drawTextAnchored(int textId,
-                                 float x,
-                                 float y,
-                                 float panX,
-                                 float panY,
-                                 int flags) {
+    public void drawTextAnchored(int textId, float x, float y, float panX, float panY, int flags) {
 
-        DrawTextAnchored.apply(
-                mBuffer, textId,
-                x, y,
-                panX, panY,
-                flags);
+        DrawTextAnchored.apply(mBuffer, textId, x, y, panX, panY, flags);
     }
 
     /**
      * draw an interpolation between two paths that have the same pattern
-     * <p>
-     * Warning paths objects are not immutable and this is not taken into consideration
+     *
+     * <p>Warning paths objects are not immutable and this is not taken into consideration
      *
      * @param path1 The path1 to be drawn between
      * @param path2 The path2 to be drawn between
      * @param tween The ratio of path1 and path2 to 0 = all path 1, 1 = all path2
      * @param start The start of the subrange of paths to draw 0 = start form start 0.5 is half way
-     * @param stop  The end of the subrange of paths to draw 1 = end at the end 0.5 is end half way
+     * @param stop The end of the subrange of paths to draw 1 = end at the end 0.5 is end half way
      */
-    public void addDrawTweenPath(Object path1,
-                                 Object path2,
-                                 float tween,
-                                 float start,
-                                 float stop) {
+    public void addDrawTweenPath(Object path1, Object path2, float tween, float start, float stop) {
         int path1Id = mRemoteComposeState.dataGetId(path1);
         if (path1Id == -1) { // never been seen before
             path1Id = addPathData(path1);
@@ -663,18 +865,12 @@
      *
      * @param path1Id The path1 to be drawn between
      * @param path2Id The path2 to be drawn between
-     * @param tween   The ratio of path1 and path2 to 0 = all path 1, 1 = all path2
-     * @param start   The start of the subrange of paths to draw 0 = start form start .5 is 1/2 way
-     * @param stop    The end of the subrange of paths to draw 1 = end at the end .5 is end 1/2 way
+     * @param tween The ratio of path1 and path2 to 0 = all path 1, 1 = all path2
+     * @param start The start of the subrange of paths to draw 0 = start form start .5 is 1/2 way
+     * @param stop The end of the subrange of paths to draw 1 = end at the end .5 is end 1/2 way
      */
-    public void addDrawTweenPath(int path1Id,
-                                 int path2Id,
-                                 float tween,
-                                 float start,
-                                 float stop) {
-        DrawTweenPath.apply(
-                mBuffer, path1Id, path2Id,
-                tween, start, stop);
+    public void addDrawTweenPath(int path1Id, int path2Id, float tween, float start, float stop) {
+        DrawTweenPath.apply(mBuffer, path1Id, path2Id, tween, start, stop);
     }
 
     /**
@@ -692,11 +888,13 @@
 
     /**
      * Adds a paint Bundle to the doc
+     *
      * @param paint
      */
     public void addPaint(PaintBundle paint) {
         PaintData.apply(mBuffer, paint);
     }
+
     ///////////////////////////////////////////////////////////////////////////////////////////////
 
     public void inflateFromBuffer(ArrayList<Operation> operations) {
@@ -741,30 +939,29 @@
         return "v1.0";
     }
 
-    public static RemoteComposeBuffer fromFile(String path,
-                                               RemoteComposeState remoteComposeState)
+    public static RemoteComposeBuffer fromFile(String path, RemoteComposeState remoteComposeState)
             throws IOException {
         RemoteComposeBuffer buffer = new RemoteComposeBuffer(remoteComposeState);
         read(new File(path), buffer);
         return buffer;
     }
 
-    public RemoteComposeBuffer fromFile(File file,
-                                        RemoteComposeState remoteComposeState) throws IOException {
+    public RemoteComposeBuffer fromFile(File file, RemoteComposeState remoteComposeState)
+            throws IOException {
         RemoteComposeBuffer buffer = new RemoteComposeBuffer(remoteComposeState);
         read(file, buffer);
         return buffer;
     }
 
-    public static RemoteComposeBuffer fromInputStream(InputStream inputStream,
-                                                      RemoteComposeState remoteComposeState) {
+    public static RemoteComposeBuffer fromInputStream(
+            InputStream inputStream, RemoteComposeState remoteComposeState) {
         RemoteComposeBuffer buffer = new RemoteComposeBuffer(remoteComposeState);
         read(inputStream, buffer);
         return buffer;
     }
 
-    RemoteComposeBuffer copyFromOperations(ArrayList<Operation> operations,
-                                           RemoteComposeBuffer buffer) {
+    RemoteComposeBuffer copyFromOperations(
+            ArrayList<Operation> operations, RemoteComposeBuffer buffer) {
 
         for (Operation operation : operations) {
             operation.write(buffer.mBuffer);
@@ -832,9 +1029,9 @@
     }
 
     /**
-     * This call balances a previous call to save(), and is used to remove all
-     * modifications to the matrix/clip state since the last save call.
-     * Do not call restore() more times than save() was called.
+     * This call balances a previous call to save(), and is used to remove all modifications to the
+     * matrix/clip state since the last save call. Do not call restore() more times than save() was
+     * called.
      */
     public void addMatrixRestore() {
         MatrixRestore.apply(mBuffer);
@@ -842,11 +1039,10 @@
 
     /**
      * Add a saves the current matrix and clip onto a private stack.
-     * <p>
-     * Subsequent calls to translate,scale,rotate,skew,concat or clipRect,
-     * clipPath will all operate as usual, but when the balancing call to
-     * restore() is made, those calls will be forgotten, and the settings that
-     * existed before the save() will be reinstated.
+     *
+     * <p>Subsequent calls to translate,scale,rotate,skew,concat or clipRect, clipPath will all
+     * operate as usual, but when the balancing call to restore() is made, those calls will be
+     * forgotten, and the settings that existed before the save() will be reinstated.
      */
     public void addMatrixSave() {
         MatrixSave.apply(mBuffer);
@@ -855,7 +1051,7 @@
     /**
      * add a pre-concat the current matrix with the specified rotation.
      *
-     * @param angle   The amount to rotate, in degrees
+     * @param angle The amount to rotate, in degrees
      * @param centerX The x-coord for the pivot point (unchanged by the rotation)
      * @param centerY The y-coord for the pivot point (unchanged by the rotation)
      */
@@ -886,8 +1082,8 @@
     /**
      * Add a pre-concat of the current matrix with the specified scale.
      *
-     * @param scaleX  The amount to scale in X
-     * @param scaleY  The amount to scale in Y
+     * @param scaleX The amount to scale in X
+     * @param scaleY The amount to scale in Y
      * @param centerX The x-coord for the pivot point (unchanged by the scale)
      * @param centerY The y-coord for the pivot point (unchanged by the scale)
      */
@@ -897,6 +1093,7 @@
 
     /**
      * sets the clip based on clip id
+     *
      * @param pathId 0 clears the clip
      */
     public void addClipPath(int pathId) {
@@ -905,10 +1102,11 @@
 
     /**
      * Sets the clip based on clip rec
-     * @param left    left coordinate of the clip rectangle
-     * @param top     top coordinate of the clip rectangle
-     * @param right   right coordinate of the clip rectangle
-     * @param bottom  bottom coordinate of the clip rectangle
+     *
+     * @param left left coordinate of the clip rectangle
+     * @param top top coordinate of the clip rectangle
+     * @param right right coordinate of the clip rectangle
+     * @param bottom bottom coordinate of the clip rectangle
      */
     public void addClipRect(float left, float top, float right, float bottom) {
         ClipRect.apply(mBuffer, left, top, right, bottom);
@@ -916,6 +1114,7 @@
 
     /**
      * Add a float return a NaN number pointing to that float
+     *
      * @param value the value of the float
      * @return the nan id of float
      */
@@ -925,9 +1124,9 @@
         return Utils.asNan(id);
     }
 
-
     /**
      * Add a Integer return an id number pointing to that float.
+     *
      * @param value adds an integer and assigns it an id
      * @return the id of the integer to be used
      */
@@ -938,7 +1137,32 @@
     }
 
     /**
+     * Add a long constant return a id. They can be used as parameters to Custom Attributes.
+     *
+     * @param value the value of the long
+     * @return the id of the command representing long
+     */
+    public int addLong(long value) {
+        int id = mRemoteComposeState.cacheData(value);
+        LongConstant.apply(mBuffer, id, value);
+        return id;
+    }
+
+    /**
+     * Add a boolean constant return a id. They can be used as parameters to Custom Attributes.
+     *
+     * @param value the value of the boolean
+     * @return the id
+     */
+    public int addBoolean(boolean value) {
+        int id = mRemoteComposeState.cacheData(value);
+        BooleanConstant.apply(mBuffer, id, value);
+        return id;
+    }
+
+    /**
      * Add a IntegerId as float ID.
+     *
      * @param id id to be converted
      * @return the id wrapped in a NaN
      */
@@ -948,6 +1172,7 @@
 
     /**
      * Add a float that is a computation based on variables
+     *
      * @param value A RPN style float operation i.e. "4, 3, ADD" outputs 7
      * @return NaN id of the result of the calculation
      */
@@ -958,8 +1183,8 @@
     }
 
     /**
-     * Add a float that is a computation based on variables.
-     * see packAnimation
+     * Add a float that is a computation based on variables. see packAnimation
+     *
      * @param value A RPN style float operation i.e. "4, 3, ADD" outputs 7
      * @param animation Array of floats that represents animation
      * @return NaN id of the result of the calculation
@@ -970,12 +1195,37 @@
         return Utils.asNan(id);
     }
 
+    /**
+     * measure the text and return a measure as a float
+     *
+     * @param textId id of the text
+     * @param mode the mode 0 is the width
+     * @return
+     */
+    public float textMeasure(int textId, int mode) {
+        int id = mRemoteComposeState.cacheData(textId + mode * 31);
+        TextMeasure.apply(mBuffer, id, textId, mode);
+        return Utils.asNan(id);
+    }
+
+    /**
+     * measure the text and return the length of the text as float
+     *
+     * @param textId id of the text
+     * @return id of a float that is the length
+     */
+    public float textLength(int textId) {
+        // The cache id is computed buy merging the two values together
+        // to create a relatively unique value
+        int id = mRemoteComposeState.cacheData(textId + (TextLength.id() << 16));
+        TextLength.apply(mBuffer, id, textId);
+        return Utils.asNan(id);
+    }
 
     /**
      * add a float array
      *
      * @param values
-     *
      * @return the id of the array, encoded as a float NaN
      */
     public float addFloatArray(float[] values) {
@@ -986,23 +1236,23 @@
 
     /**
      * This creates a list of individual floats
-     * @param values array of floats to be individually stored
      *
+     * @param values array of floats to be individually stored
      * @return id of the list
      */
     public float addFloatList(float[] values) {
-        int []listId = new int[values.length];
+        int[] listId = new int[values.length];
         for (int i = 0; i < listId.length; i++) {
             listId[i] = mRemoteComposeState.cacheFloat(values[i]);
-            FloatConstant.apply(mBuffer,  listId[i], values[i]);
+            FloatConstant.apply(mBuffer, listId[i], values[i]);
         }
         return addList(listId);
     }
 
     /**
      * This creates a list of individual floats
-     * @param listId array id to be stored
      *
+     * @param listId array id to be stored
      * @return id of the list
      */
     public float addList(int[] listId) {
@@ -1016,16 +1266,17 @@
      *
      * @param keys
      * @param values
-     *
      * @return the id of the map, encoded as a float NaN
      */
-    public float addFloatMap(String[]keys, float[] values) {
-        int []listId = new int[values.length];
+    public float addFloatMap(String[] keys, float[] values) {
+        int[] listId = new int[values.length];
+        byte[] type = new byte[values.length];
         for (int i = 0; i < listId.length; i++) {
             listId[i] = mRemoteComposeState.cacheFloat(values[i]);
-            FloatConstant.apply(mBuffer,  listId[i], values[i]);
+            FloatConstant.apply(mBuffer, listId[i], values[i]);
+            type[i] = DataMapIds.TYPE_FLOAT;
         }
-        return addMap(keys, listId);
+        return addMap(keys, type, listId);
     }
 
     /**
@@ -1033,17 +1284,49 @@
      *
      * @param keys
      * @param listId
-     *
      * @return the id of the map, encoded as a float NaN
      */
-    public float addMap(String []keys, int[] listId) {
+    public int addMap(String[] keys, byte[] types, int[] listId) {
         int id = mRemoteComposeState.cacheData(listId, NanMap.TYPE_ARRAY);
-        DataMapIds.apply(mBuffer, id, keys, listId);
-        return Utils.asNan(id);
+        DataMapIds.apply(mBuffer, id, keys, types, listId);
+        return id;
+    }
+
+    /**
+     * This provides access to text in RemoteList
+     *
+     * @param dataSet
+     * @param index index as a float variable
+     * @return
+     */
+    public int textLookup(float dataSet, float index) {
+        long hash =
+                ((long) Float.floatToRawIntBits(dataSet))
+                        << (32 + Float.floatToRawIntBits(index)); // TODO: is this the correct ()s?
+        int id = mRemoteComposeState.cacheData(hash);
+        TextLookup.apply(mBuffer, id, Utils.idFromNan(dataSet), index);
+        return id;
+    }
+
+    /**
+     * This provides access to text in RemoteList
+     *
+     * @param dataSet
+     * @param index index as an int variable
+     * @return
+     */
+    public int textLookup(float dataSet, int index) {
+        long hash =
+                ((long) Float.floatToRawIntBits(dataSet))
+                        << (32 + Float.floatToRawIntBits(index)); // TODO: is this the correct ()s?
+        int id = mRemoteComposeState.cacheData(hash);
+        TextLookupInt.apply(mBuffer, id, Utils.idFromNan(dataSet), index);
+        return id;
     }
 
     /**
      * Add and integer expression
+     *
      * @param mask defines which elements are operators or variables
      * @param value array of values to calculate maximum 32
      * @return the id as an integer
@@ -1051,11 +1334,12 @@
     public int addIntegerExpression(int mask, int[] value) {
         int id = mRemoteComposeState.cacheData(value);
         IntegerExpression.apply(mBuffer, id, mask, value);
-        return  id;
+        return id;
     }
 
     /**
      * Add a simple color
+     *
      * @param color the RGB color value
      * @return id that represents that color
      */
@@ -1067,9 +1351,9 @@
         return id;
     }
 
-
     /**
      * Add a color that represents the tween between two colors
+     *
      * @param color1 the ARGB value of the first color
      * @param color2 the ARGB value of the second color
      * @param tween the interpolation bet
@@ -1084,8 +1368,8 @@
     }
 
     /**
-     * Add a color that represents the tween between two colors where color1
-     * is the id of a color
+     * Add a color that represents the tween between two colors where color1 is the id of a color
+     *
      * @param color1 id of color
      * @param color2 rgb color value
      * @param tween the tween between color1 and color2 (1 = color2)
@@ -1100,8 +1384,8 @@
     }
 
     /**
-     * Add a color that represents the tween between two colors where color2
-     * is the id of a color
+     * Add a color that represents the tween between two colors where color2 is the id of a color
+     *
      * @param color1 the ARGB value of the first color
      * @param color2 id of the second color
      * @param tween the tween between color1 and color2 (1 = color2)
@@ -1116,8 +1400,9 @@
     }
 
     /**
-     * Add a color that represents the tween between two colors where color1 &
-     * color2 are the ids of colors
+     * Add a color that represents the tween between two colors where color1 & color2 are the ids of
+     * colors
+     *
      * @param color1 id of the first color
      * @param color2 id of the second color
      * @param tween the tween between color1 and color2 (1 = color2)
@@ -1132,8 +1417,9 @@
     }
 
     /**
-     * Color calculated by Hue saturation and value.
-     * (as floats they can be variables used to create color transitions)
+     * Color calculated by Hue saturation and value. (as floats they can be variables used to create
+     * color transitions)
+     *
      * @param hue the Hue
      * @param sat the saturation
      * @param value the value
@@ -1148,8 +1434,9 @@
     }
 
     /**
-     * Color calculated by Alpha, Hue saturation and value.
-     * (as floats they can be variables used to create color transitions)
+     * Color calculated by Alpha, Hue saturation and value. (as floats they can be variables used to
+     * create color transitions)
+     *
      * @param alpha the Alpha
      * @param hue the hue
      * @param sat the saturation
@@ -1165,8 +1452,9 @@
     }
 
     /**
-     * create and animation based on description and return as an array of
-     * floats. see addAnimatedFloat
+     * create and animation based on description and return as an array of floats. see
+     * addAnimatedFloat
+     *
      * @param duration the duration of the aimation
      * @param type the type of animation
      * @param spec the parameters of the animation if any
@@ -1174,23 +1462,20 @@
      * @param wrap the wraps value so (e.g 360 so angles 355 would animate to 5)
      * @return
      */
-    public static float[] packAnimation(float duration,
-                                        int type,
-                                        float[] spec,
-                                        float initialValue,
-                                        float wrap) {
+    public static float[] packAnimation(
+            float duration, int type, float[] spec, float initialValue, float wrap) {
 
         return FloatAnimation.packToFloatArray(duration, type, spec, initialValue, wrap);
     }
 
     /**
      * This defines the name of the color given the id.
+     *
      * @param id of the color
      * @param name Name of the color
      */
     public void setColorName(int id, String name) {
-        NamedVariable.apply(mBuffer, id,
-                NamedVariable.COLOR_TYPE, name);
+        NamedVariable.apply(mBuffer, id, NamedVariable.COLOR_TYPE, name);
     }
 
     /**
@@ -1200,16 +1485,14 @@
      * @param name name of the string
      */
     public void setStringName(int id, String name) {
-        NamedVariable.apply(mBuffer, id,
-                NamedVariable.STRING_TYPE, name);
+        NamedVariable.apply(mBuffer, id, NamedVariable.STRING_TYPE, name);
     }
 
     /**
-     * Returns a usable component id -- either the one passed in parameter if not -1
-     * or a generated one.
+     * Returns a usable component id -- either the one passed in parameter if not -1 or a generated
+     * one.
      *
      * @param id the current component id (if -1, we'll generate a new one)
-     *
      * @return a usable component id
      */
     private int getComponentId(int id) {
@@ -1225,63 +1508,64 @@
 
     /**
      * Add a component start tag
+     *
      * @param type type of component
      * @param id component id
      */
     public void addComponentStart(int type, int id) {
         mLastComponentId = getComponentId(id);
-        ComponentStart.apply(mBuffer,
-                type, mLastComponentId, 0f, 0f);
+        ComponentStart.apply(mBuffer, type, mLastComponentId, 0f, 0f);
     }
 
     /**
      * Add a component start tag
+     *
      * @param type type of component
      */
     public void addComponentStart(int type) {
         addComponentStart(type, -1);
     }
 
-    /**
-     * Add a component end tag
-     */
+    /** Add a component end tag */
     public void addComponentEnd() {
         ComponentEnd.apply(mBuffer);
     }
 
     /**
      * Add a background modifier of provided color
+     *
      * @param color the color of the background
      * @param shape the background shape -- SHAPE_RECTANGLE, SHAPE_CIRCLE
      */
     public void addModifierBackground(int color, int shape) {
-        float r = ((color >> 16) & 0xff) / 255.0f;
-        float g = ((color >> 8) & 0xff) / 255.0f;
-        float b = ((color) & 0xff) / 255.0f;
-        float a = ((color >> 24) & 0xff) / 255.0f;
-        BackgroundModifierOperation.apply(mBuffer, 0f, 0f, 0f, 0f,
-                r, g, b, a, shape);
+        float r = (color >> 16 & 0xff) / 255.0f;
+        float g = (color >> 8 & 0xff) / 255.0f;
+        float b = (color & 0xff) / 255.0f;
+        float a = (color >> 24 & 0xff) / 255.0f;
+        BackgroundModifierOperation.apply(mBuffer, 0f, 0f, 0f, 0f, r, g, b, a, shape);
     }
 
     /**
      * Add a border modifier
+     *
      * @param borderWidth the border width
      * @param borderRoundedCorner the rounded corner radius if the shape is ROUNDED_RECT
      * @param color the color of the border
      * @param shape the shape of the border
      */
-    public void addModifierBorder(float borderWidth, float borderRoundedCorner,
-                                  int color, int shape) {
-        float r = ((color >> 16) & 0xff) / 255.0f;
-        float g = ((color >>  8) & 0xff) / 255.0f;
-        float b = ((color) & 0xff) / 255.0f;
-        float a = ((color >> 24) & 0xff) / 255.0f;
-        BorderModifierOperation.apply(mBuffer, 0f, 0f, 0f, 0f,
-                borderWidth, borderRoundedCorner, r, g, b, a, shape);
+    public void addModifierBorder(
+            float borderWidth, float borderRoundedCorner, int color, int shape) {
+        float r = (color >> 16 & 0xff) / 255.0f;
+        float g = (color >> 8 & 0xff) / 255.0f;
+        float b = (color & 0xff) / 255.0f;
+        float a = (color >> 24 & 0xff) / 255.0f;
+        BorderModifierOperation.apply(
+                mBuffer, 0f, 0f, 0f, 0f, borderWidth, borderRoundedCorner, r, g, b, a, shape);
     }
 
     /**
      * Add a padding modifier
+     *
      * @param left left padding
      * @param top top padding
      * @param right right padding
@@ -1293,24 +1577,36 @@
 
     /**
      * Sets the clip based on rounded clip rect
+     *
      * @param topStart
      * @param topEnd
      * @param bottomStart
      * @param bottomEnd
      */
-    public void addRoundClipRectModifier(float topStart, float topEnd,
-                                         float bottomStart, float bottomEnd) {
-        RoundedClipRectModifierOperation.apply(mBuffer,
-                topStart, topEnd, bottomStart, bottomEnd);
+    public void addRoundClipRectModifier(
+            float topStart, float topEnd, float bottomStart, float bottomEnd) {
+        RoundedClipRectModifierOperation.apply(mBuffer, topStart, topEnd, bottomStart, bottomEnd);
     }
 
-    /**
-     * Add a clip rect modifier
-     */
+    /** Add a clip rect modifier */
     public void addClipRectModifier() {
         ClipRectModifierOperation.apply(mBuffer);
     }
 
+    public void addLoopStart(float count, float from, float step, int indexId) {
+        LoopOperation.apply(mBuffer, count, from, step, indexId);
+    }
+
+    public void addLoopEnd() {
+        LoopEnd.apply(mBuffer);
+    }
+
+    public void addStateLayout(
+            int componentId, int animationId, int horizontal, int vertical, int indexId) {
+        mLastComponentId = getComponentId(componentId);
+        StateLayout.apply(mBuffer, mLastComponentId, animationId, horizontal, vertical, indexId);
+    }
+
     /**
      * Add a box start tag
      *
@@ -1319,11 +1615,9 @@
      * @param horizontal horizontal alignment
      * @param vertical vertical alignment
      */
-    public void addBoxStart(int componentId, int animationId,
-                            int horizontal, int vertical) {
+    public void addBoxStart(int componentId, int animationId, int horizontal, int vertical) {
         mLastComponentId = getComponentId(componentId);
-        BoxLayout.apply(mBuffer, mLastComponentId, animationId,
-                horizontal, vertical);
+        BoxLayout.apply(mBuffer, mLastComponentId, animationId, horizontal, vertical);
     }
 
     /**
@@ -1335,11 +1629,10 @@
      * @param vertical vertical alignment
      * @param spacedBy spacing between items
      */
-    public void addRowStart(int componentId, int animationId,
-                            int horizontal, int vertical, float spacedBy) {
+    public void addRowStart(
+            int componentId, int animationId, int horizontal, int vertical, float spacedBy) {
         mLastComponentId = getComponentId(componentId);
-        RowLayout.apply(mBuffer, mLastComponentId, animationId,
-                horizontal, vertical, spacedBy);
+        RowLayout.apply(mBuffer, mLastComponentId, animationId, horizontal, vertical, spacedBy);
     }
 
     /**
@@ -1351,15 +1644,15 @@
      * @param vertical vertical alignment
      * @param spacedBy spacing between items
      */
-    public void addColumnStart(int componentId, int animationId,
-                            int horizontal, int vertical, float spacedBy) {
+    public void addColumnStart(
+            int componentId, int animationId, int horizontal, int vertical, float spacedBy) {
         mLastComponentId = getComponentId(componentId);
-        ColumnLayout.apply(mBuffer, mLastComponentId, animationId,
-                horizontal, vertical, spacedBy);
+        ColumnLayout.apply(mBuffer, mLastComponentId, animationId, horizontal, vertical, spacedBy);
     }
 
     /**
      * Add a canvas start tag
+     *
      * @param componentId component id
      * @param animationId animation id
      */
@@ -1370,6 +1663,7 @@
 
     /**
      * Add a canvas content start tag
+     *
      * @param componentId component id
      */
     public void addCanvasContentStart(int componentId) {
@@ -1377,17 +1671,13 @@
         CanvasContent.apply(mBuffer, mLastComponentId);
     }
 
-    /**
-     * Add a root start tag
-     */
+    /** Add a root start tag */
     public void addRootStart() {
         mLastComponentId = getComponentId(-1);
         RootLayoutComponent.apply(mBuffer, mLastComponentId);
     }
 
-    /**
-     * Add a content start tag
-     */
+    /** Add a content start tag */
     public void addContentStart() {
         mLastComponentId = getComponentId(-1);
         LayoutComponentContent.apply(mBuffer, mLastComponentId);
@@ -1395,6 +1685,7 @@
 
     /**
      * Add a component width value
+     *
      * @param id id of the value
      */
     public void addComponentWidthValue(int id) {
@@ -1422,16 +1713,29 @@
      * @param fontWeight font weight (1 to 1000, normal is 400)
      * @param fontFamily font family or null
      */
-    public void addTextComponentStart(int componentId, int animationId,
-                                      int textId, int color, float fontSize,
-                                      int fontStyle, float fontWeight, String fontFamily) {
+    public void addTextComponentStart(
+            int componentId,
+            int animationId,
+            int textId,
+            int color,
+            float fontSize,
+            int fontStyle,
+            float fontWeight,
+            String fontFamily) {
         mLastComponentId = getComponentId(componentId);
         int fontFamilyId = -1;
         if (fontFamily != null) {
             fontFamilyId = addText(fontFamily);
         }
-        TextLayout.apply(mBuffer, mLastComponentId, animationId, textId, color, fontSize,
-                fontStyle, fontWeight, fontFamilyId);
+        TextLayout.apply(
+                mBuffer,
+                mLastComponentId,
+                animationId,
+                textId,
+                color,
+                fontSize,
+                fontStyle,
+                fontWeight,
+                fontFamilyId);
     }
 }
-
diff --git a/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeOperation.java b/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeOperation.java
index c7ec3359..e60695f 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeOperation.java
@@ -15,6 +15,4 @@
  */
 package com.android.internal.widget.remotecompose.core;
 
-public interface RemoteComposeOperation extends Operation {
-
-}
+public interface RemoteComposeOperation extends Operation {}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeState.java b/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeState.java
index 839522e..51445f2 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeState.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeState.java
@@ -15,30 +15,24 @@
  */
 package com.android.internal.widget.remotecompose.core;
 
-import static com.android.internal.widget.remotecompose.core.RemoteContext.ID_CONTINUOUS_SEC;
-import static com.android.internal.widget.remotecompose.core.RemoteContext.ID_TIME_IN_MIN;
-import static com.android.internal.widget.remotecompose.core.RemoteContext.ID_TIME_IN_SEC;
-import static com.android.internal.widget.remotecompose.core.RemoteContext.ID_WINDOW_HEIGHT;
-import static com.android.internal.widget.remotecompose.core.RemoteContext.ID_WINDOW_WIDTH;
-import static com.android.internal.widget.remotecompose.core.operations.utilities.NanMap.START_ARRAY;
-import static com.android.internal.widget.remotecompose.core.operations.utilities.NanMap.START_VAR;
-
 import com.android.internal.widget.remotecompose.core.operations.utilities.ArrayAccess;
 import com.android.internal.widget.remotecompose.core.operations.utilities.CollectionsAccess;
+import com.android.internal.widget.remotecompose.core.operations.utilities.DataMap;
 import com.android.internal.widget.remotecompose.core.operations.utilities.IntFloatMap;
 import com.android.internal.widget.remotecompose.core.operations.utilities.IntIntMap;
 import com.android.internal.widget.remotecompose.core.operations.utilities.IntMap;
+import com.android.internal.widget.remotecompose.core.operations.utilities.NanMap;
 
 import java.util.ArrayList;
 import java.util.HashMap;
 
 /**
- * Represents runtime state for a RemoteCompose document
- * State includes things like the value of variables
+ * Represents runtime state for a RemoteCompose document State includes things like the value of
+ * variables
  */
 public class RemoteComposeState implements CollectionsAccess {
     public static final int START_ID = 42;
-    private static final int MAX_FLOATS = 500;
+    //    private static final int MAX_FLOATS = 500;
     private static final int MAX_COLORS = 200;
 
     private static final int MAX_DATA = 1000;
@@ -48,6 +42,8 @@
     private final IntFloatMap mFloatMap = new IntFloatMap(); // efficient cache
     private final IntIntMap mIntegerMap = new IntIntMap(); // efficient cache
     private final IntIntMap mColorMap = new IntIntMap(); // efficient cache
+    private final IntMap<DataMap> mDataMapMap = new IntMap<>();
+    private final IntMap<Object> mObjectMap = new IntMap<>();
 
     private final boolean[] mColorOverride = new boolean[MAX_COLORS];
     private final IntMap<ArrayAccess> mCollectionMap = new IntMap<>();
@@ -56,13 +52,12 @@
     private final boolean[] mIntegerOverride = new boolean[MAX_DATA];
 
     private int mNextId = START_ID;
-    private int[] mIdMaps = new int[]{START_ID, START_VAR, START_ARRAY};
+    private int[] mIdMaps = new int[] {START_ID, NanMap.START_VAR, NanMap.START_ARRAY};
     private RemoteContext mRemoteContext = null;
 
-
     /**
-     * Get Object based on id. The system will cache things like bitmaps
-     * Paths etc. They can be accessed with this command
+     * Get Object based on id. The system will cache things like bitmaps Paths etc. They can be
+     * accessed with this command
      *
      * @param id
      * @return
@@ -81,9 +76,7 @@
         return mIntDataMap.get(id) != null;
     }
 
-    /**
-     * Return the id of an item from the cache.
-     */
+    /** Return the id of an item from the cache. */
     public int dataGetId(Object data) {
         Integer res = mDataIntMap.get(data);
         if (res == null) {
@@ -93,8 +86,8 @@
     }
 
     /**
-     * Add an item to the cache. Generates an id for the item and adds it to the cache based on
-     * that id.
+     * Add an item to the cache. Generates an id for the item and adds it to the cache based on that
+     * id.
      */
     public int cacheData(Object item) {
         int id = nextId();
@@ -104,8 +97,8 @@
     }
 
     /**
-     * Add an item to the cache. Generates an id for the item and adds it to the cache based on
-     * that id.
+     * Add an item to the cache. Generates an id for the item and adds it to the cache based on that
+     * id.
      */
     public int cacheData(Object item, int type) {
         int id = nextId(type);
@@ -114,23 +107,22 @@
         return id;
     }
 
-    /**
-     * Insert an item in the cache
-     */
+    /** Insert an item in the cache */
     public void cacheData(int id, Object item) {
         mDataIntMap.put(item, id);
         mIntDataMap.put(id, item);
     }
 
-    /**
-     * Insert an item in the cache
-     */
+    /** Insert an item in the cache */
     public void updateData(int id, Object item) {
         if (!mDataOverride[id]) {
-            mDataIntMap.remove(mIntDataMap.get(id));
-            mDataIntMap.put(item, id);
-            mIntDataMap.put(id, item);
-            updateListeners(id);
+            Object previous = mIntDataMap.get(id);
+            if (previous != item) {
+                mDataIntMap.remove(previous);
+                mDataIntMap.put(item, id);
+                mIntDataMap.put(id, item);
+                updateListeners(id);
+            }
         }
     }
 
@@ -141,16 +133,17 @@
      * @param item the new value
      */
     public void overrideData(int id, Object item) {
-        mDataIntMap.remove(mIntDataMap.get(id));
-        mDataIntMap.put(item, id);
-        mIntDataMap.put(id, item);
-        mDataOverride[id] = true;
-        updateListeners(id);
+        Object previous = mIntDataMap.get(id);
+        if (previous != item) {
+            mDataIntMap.remove(previous);
+            mDataIntMap.put(item, id);
+            mIntDataMap.put(id, item);
+            mDataOverride[id] = true;
+            updateListeners(id);
+        }
     }
 
-    /**
-     * Insert an item in the cache
-     */
+    /** Insert an item in the cache */
     public int cacheFloat(float item) {
         int id = nextId();
         mFloatMap.put(id, item);
@@ -158,25 +151,22 @@
         return id;
     }
 
-    /**
-     * Insert an item in the cache
-     */
+    /** Insert an item in the cache */
     public void cacheFloat(int id, float item) {
         mFloatMap.put(id, item);
     }
 
-    /**
-     * Insert an float item in the cache
-     */
-    public void updateFloat(int id, float item) {
-        mFloatMap.put(id, item);
-        mIntegerMap.put(id, (int) item);
-        updateListeners(id);
+    /** Insert an float item in the cache */
+    public void updateFloat(int id, float value) {
+        float previous = mFloatMap.get(id);
+        if (previous != value) {
+            mFloatMap.put(id, value);
+            mIntegerMap.put(id, (int) value);
+            updateListeners(id);
+        }
     }
 
-    /**
-     * Insert an item in the cache
-     */
+    /** Insert an item in the cache */
     public int cacheInteger(int item) {
         int id = nextId();
         mIntegerMap.put(id, item);
@@ -184,14 +174,15 @@
         return id;
     }
 
-    /**
-     * Insert an integer item in the cache
-     */
-    public void updateInteger(int id, int item) {
+    /** Insert an integer item in the cache */
+    public void updateInteger(int id, int value) {
         if (!mIntegerOverride[id]) {
-            mFloatMap.put(id, item);
-            mIntegerMap.put(id, item);
-            updateListeners(id);
+            int previous = mIntegerMap.get(id);
+            if (previous != value) {
+                mFloatMap.put(id, value);
+                mIntegerMap.put(id, value);
+                updateListeners(id);
+            }
         }
     }
 
@@ -202,10 +193,13 @@
      * @param value the new value
      */
     public void overrideInteger(int id, int value) {
-        mIntegerMap.put(id, value);
-        mFloatMap.put(id, value);
-        mIntegerOverride[id] = true;
-        updateListeners(id);
+        int previous = mIntegerMap.get(id);
+        if (previous != value) {
+            mIntegerMap.put(id, value);
+            mFloatMap.put(id, value);
+            mIntegerOverride[id] = true;
+            updateListeners(id);
+        }
     }
 
     /**
@@ -262,8 +256,7 @@
     }
 
     /**
-     * Adds a colorOverride.
-     * This is a list of ids and their colors optimized for playback;
+     * Adds a colorOverride. This is a list of ids and their colors optimized for playback;
      *
      * @param id
      * @param color
@@ -273,9 +266,7 @@
         mColorMap.put(id, color);
     }
 
-    /**
-     * Clear the color Overrides
-     */
+    /** Clear the color Overrides */
     public void clearColorOverride() {
         for (int i = 0; i < mColorOverride.length; i++) {
             mColorOverride[i] = false;
@@ -310,16 +301,12 @@
         return !mIntWrittenMap.get(id);
     }
 
-    /**
-     * Method to mark that a value, represented by its id, has been written to the WireBuffer
-     */
+    /** Method to mark that a value, represented by its id, has been written to the WireBuffer */
     public void markWritten(int id) {
         mIntWrittenMap.put(id, true);
     }
 
-    /**
-     * Clear the record of the values that have been written to the WireBuffer.
-     */
+    /** Clear the record of the values that have been written to the WireBuffer. */
     public void reset() {
         mIntWrittenMap.clear();
         mDataIntMap.clear();
@@ -388,13 +375,13 @@
         for (VariableSupport vs : mAllVarListeners) {
             vs.updateVariables(context);
         }
-        if (mVarListeners.get(ID_CONTINUOUS_SEC) != null) {
+        if (mVarListeners.get(RemoteContext.ID_CONTINUOUS_SEC) != null) {
             return 1;
         }
-        if (mVarListeners.get(ID_TIME_IN_SEC) != null) {
+        if (mVarListeners.get(RemoteContext.ID_TIME_IN_SEC) != null) {
             return 1000;
         }
-        if (mVarListeners.get(ID_TIME_IN_MIN) != null) {
+        if (mVarListeners.get(RemoteContext.ID_TIME_IN_MIN) != null) {
             return 1000 * 60;
         }
         return -1;
@@ -406,7 +393,7 @@
      * @param width
      */
     public void setWindowWidth(float width) {
-        updateFloat(ID_WINDOW_WIDTH, width);
+        updateFloat(RemoteContext.ID_WINDOW_WIDTH, width);
     }
 
     /**
@@ -415,7 +402,7 @@
      * @param height
      */
     public void setWindowHeight(float height) {
-        updateFloat(ID_WINDOW_HEIGHT, height);
+        updateFloat(RemoteContext.ID_WINDOW_HEIGHT, height);
     }
 
     public void addCollection(int id, ArrayAccess collection) {
@@ -426,17 +413,39 @@
     public float getFloatValue(int id, int index) {
         return mCollectionMap.get(id & 0xFFFFF).getFloatValue(index);
     }
+
     @Override
     public float[] getFloats(int id) {
         return mCollectionMap.get(id & 0xFFFFF).getFloats();
     }
 
     @Override
-    public int getFloatsLength(int id) {
-        return mCollectionMap.get(id & 0xFFFFF).getFloatsLength();
+    public int getId(int id, int index) {
+        return mCollectionMap.get(id & 0xFFFFF).getId(index);
+    }
+
+    public void putDataMap(int id, DataMap map) {
+        mDataMapMap.put(id, map);
+    }
+
+    public DataMap getDataMap(int id) {
+        return mDataMapMap.get(id);
+    }
+
+    @Override
+    public int getListLength(int id) {
+        return mCollectionMap.get(id & 0xFFFFF).getLength();
     }
 
     public void setContext(RemoteContext context) {
         mRemoteContext = context;
     }
+
+    public void updateObject(int id, Object value) {
+        mObjectMap.put(id, value);
+    }
+
+    public Object getObject(int id) {
+        return mObjectMap.get(id);
+    }
 }
diff --git a/core/java/com/android/internal/widget/remotecompose/core/RemoteContext.java b/core/java/com/android/internal/widget/remotecompose/core/RemoteContext.java
index 0df0aa0..1066e7d 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/RemoteContext.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/RemoteContext.java
@@ -22,6 +22,7 @@
 import com.android.internal.widget.remotecompose.core.operations.layout.Component;
 import com.android.internal.widget.remotecompose.core.operations.utilities.ArrayAccess;
 import com.android.internal.widget.remotecompose.core.operations.utilities.CollectionsAccess;
+import com.android.internal.widget.remotecompose.core.operations.utilities.DataMap;
 
 import java.time.LocalDateTime;
 import java.time.OffsetDateTime;
@@ -31,9 +32,9 @@
 /**
  * Specify an abstract context used to playback RemoteCompose documents
  *
- * This allows us to intercept the different operations in a document and react to them.
+ * <p>This allows us to intercept the different operations in a document and react to them.
  *
- * We also contain a PaintContext, so that any operation can draw as needed.
+ * <p>We also contain a PaintContext, so that any operation can draw as needed.
  */
 public abstract class RemoteContext {
     protected CoreDocument mDocument;
@@ -73,8 +74,7 @@
     }
 
     /**
-     * Load a path under an id.
-     * Paths can be use in clip drawPath and drawTweenPath
+     * Load a path under an id. Paths can be use in clip drawPath and drawTweenPath
      *
      * @param instanceId
      * @param floatPath
@@ -85,7 +85,7 @@
      * Associate a name with a give id.
      *
      * @param varName the name
-     * @param varId   the id (color,integer,float etc.)
+     * @param varId the id (color,integer,float etc.)
      * @param varType thetype
      */
     public abstract void loadVariableName(String varName, int varId, int varType);
@@ -93,7 +93,7 @@
     /**
      * Save a color under a given id
      *
-     * @param id    the id of the color
+     * @param id the id of the color
      * @param color the color to set
      */
     public abstract void loadColor(int id, int color);
@@ -118,35 +118,33 @@
     }
 
     /**
-     * Set the value of a named Color.
-     * This overrides the color in the document
+     * Set the value of a named Color. This overrides the color in the document
      *
      * @param colorName the name of the color to override
-     * @param color     Override the default color
+     * @param color Override the default color
      */
     public abstract void setNamedColorOverride(String colorName, int color);
 
     /**
-     * Set the value of a named String.
-     * This overrides the string in the document
+     * Set the value of a named String. This overrides the string in the document
+     *
      * @param stringName the name of the string to override
      * @param value Override the default string
      */
     public abstract void setNamedStringOverride(String stringName, String value);
 
-
     /**
      * Allows to clear a named String.
      *
-     * If an override exists, we revert back to the default value in the document.
+     * <p>If an override exists, we revert back to the default value in the document.
      *
      * @param stringName the name of the string to override
      */
     public abstract void clearNamedStringOverride(String stringName);
 
     /**
-     * Set the value of a named Integer.
-     * This overrides the integer in the document
+     * Set the value of a named Integer. This overrides the integer in the document
+     *
      * @param integerName the name of the integer to override
      * @param value Override the default integer
      */
@@ -155,34 +153,41 @@
     /**
      * Allows to clear a named Integer.
      *
-     * If an override exists, we revert back to the default value in the document.
+     * <p>If an override exists, we revert back to the default value in the document.
      *
      * @param integerName the name of the integer to override
      */
     public abstract void clearNamedIntegerOverride(String integerName);
 
-
     /**
      * Support Collections by registering this collection
      *
-     * @param id         id of the collection
+     * @param id id of the collection
      * @param collection the collection under this id
      */
     public abstract void addCollection(int id, ArrayAccess collection);
 
+    public abstract void putDataMap(int id, DataMap map);
+
+    public abstract DataMap getDataMap(int id);
+
     public abstract void runAction(int id, String metadata);
 
     public abstract void runNamedAction(int textId);
 
+    public abstract void putObject(int mId, Object command);
+
+    public abstract Object getObject(int mId);
 
     /**
      * The context can be used in a few different mode, allowing operations to skip being executed:
-     * - UNSET : all operations will get executed
-     * - DATA : only operations dealing with DATA (eg loading a bitmap) should execute
-     * - PAINT : only operations painting should execute
+     * - UNSET : all operations will get executed - DATA : only operations dealing with DATA (eg
+     * loading a bitmap) should execute - PAINT : only operations painting should execute
      */
     public enum ContextMode {
-        UNSET, DATA, PAINT
+        UNSET,
+        DATA,
+        PAINT
     }
 
     public int getTheme() {
@@ -229,9 +234,13 @@
     // Operations
     ///////////////////////////////////////////////////////////////////////////////////////////////
 
-    public void header(int majorVersion, int minorVersion, int patchVersion,
-                       int width, int height, long capabilities
-    ) {
+    public void header(
+            int majorVersion,
+            int minorVersion,
+            int patchVersion,
+            int width,
+            int height,
+            long capabilities) {
         mRemoteComposeState.setWindowWidth(width);
         mRemoteComposeState.setWindowHeight(height);
         mDocument.setVersion(majorVersion, minorVersion, patchVersion);
@@ -243,21 +252,14 @@
     /**
      * Sets the way the player handles the content
      *
-     * @param scroll    set the horizontal behavior (NONE|SCROLL_HORIZONTAL|SCROLL_VERTICAL)
+     * @param scroll set the horizontal behavior (NONE|SCROLL_HORIZONTAL|SCROLL_VERTICAL)
      * @param alignment set the alignment of the content (TOP|CENTER|BOTTOM|START|END)
-     * @param sizing    set the type of sizing for the content (NONE|SIZING_LAYOUT|SIZING_SCALE)
-     * @param mode      set the mode of sizing, either LAYOUT modes or SCALE modes
-     *                  the LAYOUT modes are:
-     *                  - LAYOUT_MATCH_PARENT
-     *                  - LAYOUT_WRAP_CONTENT
-     *                  or adding an horizontal mode and a vertical mode:
-     *                  - LAYOUT_HORIZONTAL_MATCH_PARENT
-     *                  - LAYOUT_HORIZONTAL_WRAP_CONTENT
-     *                  - LAYOUT_HORIZONTAL_FIXED
-     *                  - LAYOUT_VERTICAL_MATCH_PARENT
-     *                  - LAYOUT_VERTICAL_WRAP_CONTENT
-     *                  - LAYOUT_VERTICAL_FIXED
-     *                  The LAYOUT_*_FIXED modes will use the intrinsic document size
+     * @param sizing set the type of sizing for the content (NONE|SIZING_LAYOUT|SIZING_SCALE)
+     * @param mode set the mode of sizing, either LAYOUT modes or SCALE modes the LAYOUT modes are:
+     *     - LAYOUT_MATCH_PARENT - LAYOUT_WRAP_CONTENT or adding an horizontal mode and a vertical
+     *     mode: - LAYOUT_HORIZONTAL_MATCH_PARENT - LAYOUT_HORIZONTAL_WRAP_CONTENT -
+     *     LAYOUT_HORIZONTAL_FIXED - LAYOUT_VERTICAL_MATCH_PARENT - LAYOUT_VERTICAL_WRAP_CONTENT -
+     *     LAYOUT_VERTICAL_FIXED The LAYOUT_*_FIXED modes will use the intrinsic document size
      */
     public void setRootContentBehavior(int scroll, int alignment, int sizing, int mode) {
         mDocument.setRootContentBehavior(scroll, alignment, sizing, mode);
@@ -281,16 +283,16 @@
      * Save a bitmap under an imageId
      *
      * @param imageId the id of the image
-     * @param width   the width of the image
-     * @param height  the height of the image
-     * @param bitmap  the bytes that represent the image
+     * @param width the width of the image
+     * @param height the height of the image
+     * @param bitmap the bytes that represent the image
      */
     public abstract void loadBitmap(int imageId, int width, int height, byte[] bitmap);
 
     /**
      * Save a string under a given id
      *
-     * @param id   the id of the string
+     * @param id the id of the string
      * @param text the value to set
      */
     public abstract void loadText(int id, String text);
@@ -306,7 +308,7 @@
     /**
      * Load a float
      *
-     * @param id    id of the float
+     * @param id id of the float
      * @param value the value to set
      */
     public abstract void loadFloat(int id, float value);
@@ -314,21 +316,19 @@
     /**
      * Load a integer
      *
-     * @param id    id of the integer
+     * @param id id of the integer
      * @param value the value to set
      */
     public abstract void loadInteger(int id, int value);
 
-
     public abstract void overrideInteger(int id, int value);
 
     public abstract void overrideText(int id, int valueId);
 
     /**
-     * Load an animated float associated with an id
-     * Todo: Remove?
+     * Load an animated float associated with an id Todo: Remove?
      *
-     * @param id            the id of the float
+     * @param id the id of the float
      * @param animatedFloat The animated float
      */
     public abstract void loadAnimatedFloat(int id, FloatExpression animatedFloat);
@@ -336,7 +336,7 @@
     /**
      * Save a shader under and ID
      *
-     * @param id    the id of the Shader
+     * @param id the id of the Shader
      * @param value the shader
      */
     public abstract void loadShader(int id, ShaderData value);
@@ -368,7 +368,7 @@
     /**
      * called to notify system that a command is interested in a variable
      *
-     * @param id              track when this id changes value
+     * @param id track when this id changes value
      * @param variableSupport call back when value changes
      */
     public abstract void listensTo(int id, VariableSupport variableSupport);
@@ -401,33 +401,25 @@
     public static final int ID_WEEK_DAY = 11;
     public static final int ID_DAY_OF_MONTH = 12;
 
-    /**
-     * CONTINUOUS_SEC is seconds from midnight looping every hour 0-3600
-     */
+    /** CONTINUOUS_SEC is seconds from midnight looping every hour 0-3600 */
     public static final float FLOAT_CONTINUOUS_SEC = Utils.asNan(ID_CONTINUOUS_SEC);
-    /**
-     * seconds run from Midnight=0 quantized to seconds hour 0..3599
-     */
+
+    /** seconds run from Midnight=0 quantized to seconds hour 0..3599 */
     public static final float FLOAT_TIME_IN_SEC = Utils.asNan(ID_TIME_IN_SEC);
-    /**
-     * minutes run from Midnight=0 quantized to minutes 0..1439
-     */
+
+    /** minutes run from Midnight=0 quantized to minutes 0..1439 */
     public static final float FLOAT_TIME_IN_MIN = Utils.asNan(ID_TIME_IN_MIN);
-    /**
-     * hours run from Midnight=0 quantized to Hours 0-23
-     */
+
+    /** hours run from Midnight=0 quantized to Hours 0-23 */
     public static final float FLOAT_TIME_IN_HR = Utils.asNan(ID_TIME_IN_HR);
-    /**
-     * Moth of Year quantized to MONTHS 1-12. 1 = January
-     */
+
+    /** Moth of Year quantized to MONTHS 1-12. 1 = January */
     public static final float FLOAT_CALENDAR_MONTH = Utils.asNan(ID_CALENDAR_MONTH);
-    /**
-     * DAY OF THE WEEK 1-7. 1 = Monday
-     */
+
+    /** DAY OF THE WEEK 1-7. 1 = Monday */
     public static final float FLOAT_WEEK_DAY = Utils.asNan(ID_WEEK_DAY);
-    /**
-     * DAY OF THE MONTH 1-31
-     */
+
+    /** DAY OF THE MONTH 1-31 */
     public static final float FLOAT_DAY_OF_MONTH = Utils.asNan(ID_DAY_OF_MONTH);
 
     public static final float FLOAT_WINDOW_WIDTH = Utils.asNan(ID_WINDOW_WIDTH);
@@ -446,7 +438,8 @@
     }
 
     public static float getTime(float fl) {
-        LocalDateTime dateTime = LocalDateTime.now();
+        LocalDateTime dateTime =
+                LocalDateTime.now(ZoneId.systemDefault()); // TODO, pass in a timezone explicitly?
         // This define the time in the format
         // seconds run from Midnight=0 quantized to seconds hour 0..3599
         // minutes run from Midnight=0 quantized to minutes 0..1439
@@ -464,7 +457,6 @@
         float sec = currentSeconds + dateTime.getNano() * 1E-9f;
         int day_week = dateTime.getDayOfWeek().getValue();
 
-
         ZoneId zone = ZoneId.systemDefault();
         OffsetDateTime offsetDateTime = dateTime.atZone(zone).toOffsetDateTime();
         ZoneOffset offset = offsetDateTime.getOffset();
@@ -488,8 +480,6 @@
         return fl;
     }
 
-
-
     public abstract void addClickArea(
             int id,
             int contentDescription,
@@ -497,7 +487,5 @@
             float top,
             float right,
             float bottom,
-            int metadataId
-    );
+            int metadataId);
 }
-
diff --git a/core/java/com/android/internal/widget/remotecompose/core/TimeVariables.java b/core/java/com/android/internal/widget/remotecompose/core/TimeVariables.java
index 04e04bbb..fa0cf3f 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/TimeVariables.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/TimeVariables.java
@@ -20,9 +20,7 @@
 import java.time.ZoneId;
 import java.time.ZoneOffset;
 
-/**
- * This generates the standard system variables for time.
- */
+/** This generates the standard system variables for time. */
 public class TimeVariables {
     /**
      * This class populates all time variables in the system
@@ -30,7 +28,8 @@
      * @param context
      */
     public void updateTime(RemoteContext context) {
-        LocalDateTime dateTime = LocalDateTime.now();
+        LocalDateTime dateTime =
+                LocalDateTime.now(ZoneId.systemDefault()); // TODO, pass in a timezone explicitly?
         // This define the time in the format
         // seconds run from Midnight=0 quantized to seconds hour 0..3599
         // minutes run from Midnight=0 quantized to minutes 0..1439
@@ -47,7 +46,6 @@
         float sec = currentSeconds + dateTime.getNano() * 1E-9f;
         int day_week = dateTime.getDayOfWeek().getValue();
 
-
         ZoneId zone = ZoneId.systemDefault();
         OffsetDateTime offsetDateTime = dateTime.atZone(zone).toOffsetDateTime();
         ZoneOffset offset = offsetDateTime.getOffset();
@@ -60,6 +58,5 @@
         context.loadFloat(RemoteContext.ID_CALENDAR_MONTH, month);
         context.loadFloat(RemoteContext.ID_DAY_OF_MONTH, month);
         context.loadFloat(RemoteContext.ID_WEEK_DAY, day_week);
-
     }
 }
diff --git a/core/java/com/android/internal/widget/remotecompose/core/VariableSupport.java b/core/java/com/android/internal/widget/remotecompose/core/VariableSupport.java
index ee6d579..51e58a1 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/VariableSupport.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/VariableSupport.java
@@ -16,20 +16,21 @@
 package com.android.internal.widget.remotecompose.core;
 
 /**
- * Interface for operators that interact with variables
- * Through this they register to listen to particular variables
- * and are notified when they change
+ * Interface for operators that interact with variables Through this they register to listen to
+ * particular variables and are notified when they change
  */
 public interface VariableSupport {
     /**
-     * Call to allow an operator to register interest in variables.
-     * Typically they call context.listensTo(id, this)
+     * Call to allow an operator to register interest in variables. Typically they call
+     * context.listensTo(id, this)
+     *
      * @param context
      */
     void registerListening(RemoteContext context);
 
     /**
      * Called to be notified that the variables you are interested have changed.
+     *
      * @param context
      */
     void updateVariables(RemoteContext context);
diff --git a/core/java/com/android/internal/widget/remotecompose/core/WireBuffer.java b/core/java/com/android/internal/widget/remotecompose/core/WireBuffer.java
index fc3202e..c71b490 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/WireBuffer.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/WireBuffer.java
@@ -17,9 +17,7 @@
 
 import java.util.Arrays;
 
-/**
- * The base communication buffer capable of encoding and decoding various types
- */
+/** The base communication buffer capable of encoding and decoding various types */
 public class WireBuffer {
     private static final int BUFFER_SIZE = 1024 * 1024 * 1;
     int mMaxSize;
@@ -130,6 +128,7 @@
         int v2 = (mBuffer[mIndex++] & 0xFF) << 0;
         return v1 + v2;
     }
+
     public int peekInt() {
         int tmp = mIndex;
         int v1 = (mBuffer[tmp++] & 0xFF) << 24;
@@ -177,8 +176,8 @@
     public byte[] readBuffer(int maxSize) {
         int count = readInt();
         if (count < 0 || count > maxSize) {
-            throw new RuntimeException("attempt read a buff of invalid size 0 <= "
-                    + count + " > " + maxSize);
+            throw new RuntimeException(
+                    "attempt read a buff of invalid size 0 <= " + count + " > " + maxSize);
         }
         byte[] b = Arrays.copyOfRange(mBuffer, mIndex, mIndex + count);
         mIndex += count;
@@ -201,7 +200,7 @@
 
     public void writeBoolean(boolean value) {
         resize(1);
-        mBuffer[mIndex++] = (byte) ((value) ? 1 : 0);
+        mBuffer[mIndex++] = (byte) (value ? 1 : 0);
         mSize++;
     }
 
@@ -256,7 +255,6 @@
         writeInt(b.length);
         for (int i = 0; i < b.length; i++) {
             mBuffer[mIndex++] = b[i];
-
         }
         mSize += b.length;
     }
@@ -265,6 +263,4 @@
         byte[] buffer = content.getBytes();
         writeBuffer(buffer);
     }
-
 }
-
diff --git a/core/java/com/android/internal/widget/remotecompose/core/documentation/DocumentationBuilder.java b/core/java/com/android/internal/widget/remotecompose/core/documentation/DocumentationBuilder.java
index ccbcdf6..f6dfe2e 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/documentation/DocumentationBuilder.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/documentation/DocumentationBuilder.java
@@ -17,6 +17,8 @@
 
 public interface DocumentationBuilder {
     void add(String value);
-    Operation operation(String category, int id, String name);
-    Operation wipOperation(String category, int id, String name);
+
+    DocumentedOperation operation(String category, int id, String name);
+
+    DocumentedOperation wipOperation(String category, int id, String name);
 }
diff --git a/core/java/com/android/internal/widget/remotecompose/core/documentation/DocumentedOperation.java b/core/java/com/android/internal/widget/remotecompose/core/documentation/DocumentedOperation.java
new file mode 100644
index 0000000..c33ae24
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/documentation/DocumentedOperation.java
@@ -0,0 +1,181 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.widget.remotecompose.core.documentation;
+
+import java.util.ArrayList;
+
+public class DocumentedOperation {
+    public static final int LAYOUT = 0;
+    public static final int INT = 0;
+    public static final int FLOAT = 1;
+    public static final int BOOLEAN = 2;
+    public static final int BUFFER = 4;
+    public static final int UTF8 = 5;
+    public static final int BYTE = 6;
+    public static final int VALUE = 7;
+    public static final int LONG = 8;
+    public static final int SHORT = 9;
+
+    public static final int FLOAT_ARRAY = 10;
+    public static final int INT_ARRAY = 11;
+
+    String mCategory;
+    int mId;
+    String mName;
+    String mDescription;
+
+    boolean mWIP;
+    String mTextExamples;
+
+    ArrayList<StringPair> mExamples = new ArrayList<>();
+    ArrayList<OperationField> mFields = new ArrayList<>();
+    String mVarSize = "";
+    int mExamplesWidth = 100;
+    int mExamplesHeight = 100;
+
+    public static String getType(int type) {
+        switch (type) {
+            case INT:
+                return "INT";
+            case FLOAT:
+                return "FLOAT";
+            case BOOLEAN:
+                return "BOOLEAN";
+            case BUFFER:
+                return "BUFFER";
+            case UTF8:
+                return "UTF8";
+            case BYTE:
+                return "BYTE";
+            case VALUE:
+                return "VALUE";
+            case LONG:
+                return "LONG";
+            case SHORT:
+                return "SHORT";
+            case FLOAT_ARRAY:
+                return "FLOAT[]";
+            case INT_ARRAY:
+                return "INT[]";
+        }
+        return "UNKNOWN";
+    }
+
+    public DocumentedOperation(String category, int id, String name, boolean wip) {
+        mCategory = category;
+        mId = id;
+        mName = name;
+        mWIP = wip;
+    }
+
+    public DocumentedOperation(String category, int id, String name) {
+        this(category, id, name, false);
+    }
+
+    public ArrayList<OperationField> getFields() {
+        return mFields;
+    }
+
+    public String getCategory() {
+        return mCategory;
+    }
+
+    public int getId() {
+        return mId;
+    }
+
+    public String getName() {
+        return mName;
+    }
+
+    public boolean isWIP() {
+        return mWIP;
+    }
+
+    public String getVarSize() {
+        return mVarSize;
+    }
+
+    public int getSizeFields() {
+        int size = 0;
+        mVarSize = "";
+        for (OperationField field : mFields) {
+            size += Math.max(0, field.getSize());
+            if (field.getSize() < 0) {
+                mVarSize += " + " + field.getVarSize() + " x 4";
+            }
+        }
+        return size;
+    }
+
+    public String getDescription() {
+        return mDescription;
+    }
+
+    public String getTextExamples() {
+        return mTextExamples;
+    }
+
+    public ArrayList<StringPair> getExamples() {
+        return mExamples;
+    }
+
+    public int getExamplesWidth() {
+        return mExamplesWidth;
+    }
+
+    public int getExamplesHeight() {
+        return mExamplesHeight;
+    }
+
+    public DocumentedOperation field(int type, String name, String description) {
+        mFields.add(new OperationField(type, name, description));
+        return this;
+    }
+
+    public DocumentedOperation field(int type, String name, String varSize, String description) {
+        mFields.add(new OperationField(type, name, varSize, description));
+        return this;
+    }
+
+    public DocumentedOperation possibleValues(String name, int value) {
+        if (!mFields.isEmpty()) {
+            mFields.get(mFields.size() - 1).possibleValue(name, "" + value);
+        }
+        return this;
+    }
+
+    public DocumentedOperation description(String description) {
+        mDescription = description;
+        return this;
+    }
+
+    public DocumentedOperation examples(String examples) {
+        mTextExamples = examples;
+        return this;
+    }
+
+    public DocumentedOperation exampleImage(String name, String imagePath) {
+        mExamples.add(new StringPair(name, imagePath));
+        return this;
+    }
+
+    public DocumentedOperation examplesDimension(int width, int height) {
+        mExamplesWidth = width;
+        mExamplesHeight = height;
+        return this;
+    }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/documentation/Operation.java b/core/java/com/android/internal/widget/remotecompose/core/documentation/Operation.java
index 9ccb2be..f02a385 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/documentation/Operation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/documentation/Operation.java
@@ -46,20 +46,30 @@
     int mExamplesWidth = 100;
     int mExamplesHeight = 100;
 
-
     public static String getType(int type) {
         switch (type) {
-            case (INT): return "INT";
-            case (FLOAT): return "FLOAT";
-            case (BOOLEAN): return "BOOLEAN";
-            case (BUFFER): return "BUFFER";
-            case (UTF8): return "UTF8";
-            case (BYTE): return "BYTE";
-            case (VALUE): return "VALUE";
-            case (LONG): return "LONG";
-            case (SHORT): return "SHORT";
-            case (FLOAT_ARRAY): return "FLOAT[]";
-            case (INT_ARRAY): return "INT[]";
+            case INT:
+                return "INT";
+            case FLOAT:
+                return "FLOAT";
+            case BOOLEAN:
+                return "BOOLEAN";
+            case BUFFER:
+                return "BUFFER";
+            case UTF8:
+                return "UTF8";
+            case BYTE:
+                return "BYTE";
+            case VALUE:
+                return "VALUE";
+            case LONG:
+                return "LONG";
+            case SHORT:
+                return "SHORT";
+            case FLOAT_ARRAY:
+                return "FLOAT[]";
+            case INT_ARRAY:
+                return "INT[]";
         }
         return "UNKNOWN";
     }
diff --git a/core/java/com/android/internal/widget/remotecompose/core/documentation/OperationField.java b/core/java/com/android/internal/widget/remotecompose/core/documentation/OperationField.java
index 0dd3039..c770483 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/documentation/OperationField.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/documentation/OperationField.java
@@ -41,20 +41,23 @@
     public int getType() {
         return mType;
     }
+
     public String getName() {
         return mName;
     }
+
     public String getDescription() {
         return mDescription;
     }
+
     public ArrayList<StringPair> getPossibleValues() {
         return mPossibleValues;
     }
 
-
     public void possibleValue(String name, String value) {
         mPossibleValues.add(new StringPair(name, value));
     }
+
     public boolean hasEnumeratedValues() {
         return !mPossibleValues.isEmpty();
     }
@@ -65,14 +68,22 @@
 
     public int getSize() {
         switch (mType) {
-            case (Operation.BYTE) : return 1;
-            case (Operation.INT) : return 4;
-            case (Operation.FLOAT) : return 4;
-            case (Operation.LONG) : return 8;
-            case (Operation.SHORT) : return 2;
-            case (Operation.INT_ARRAY): return -1;
-            case (Operation.FLOAT_ARRAY): return -1;
-            default : return 0;
+            case DocumentedOperation.BYTE:
+                return 1;
+            case DocumentedOperation.INT:
+                return 4;
+            case DocumentedOperation.FLOAT:
+                return 4;
+            case DocumentedOperation.LONG:
+                return 8;
+            case DocumentedOperation.SHORT:
+                return 2;
+            case DocumentedOperation.INT_ARRAY:
+                return -1;
+            case DocumentedOperation.FLOAT_ARRAY:
+                return -1;
+            default:
+                return 0;
         }
     }
 }
diff --git a/core/java/com/android/internal/widget/remotecompose/core/documentation/StringPair.java b/core/java/com/android/internal/widget/remotecompose/core/documentation/StringPair.java
index 787bb54..5b0cedb 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/documentation/StringPair.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/documentation/StringPair.java
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 package com.android.internal.widget.remotecompose.core.documentation;
+
 public class StringPair {
     String mName;
     String mValue;
@@ -26,6 +27,7 @@
     public String getName() {
         return mName;
     }
+
     public String getValue() {
         return mValue;
     }
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/BitmapData.java b/core/java/com/android/internal/widget/remotecompose/core/operations/BitmapData.java
index 58be641..20ba8c31 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/BitmapData.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/BitmapData.java
@@ -15,8 +15,8 @@
  */
 package com.android.internal.widget.remotecompose.core.operations;
 
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT_ARRAY;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT_ARRAY;
 
 import com.android.internal.widget.remotecompose.core.Operation;
 import com.android.internal.widget.remotecompose.core.Operations;
@@ -24,14 +24,14 @@
 import com.android.internal.widget.remotecompose.core.SerializableToString;
 import com.android.internal.widget.remotecompose.core.WireBuffer;
 import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
 import com.android.internal.widget.remotecompose.core.operations.utilities.StringSerializer;
 
 import java.util.List;
 
 /**
- * Operation to deal with bitmap data
- * On getting an Image during a draw call the bitmap is compressed and saved
- * in playback the image is decompressed
+ * Operation to deal with bitmap data On getting an Image during a draw call the bitmap is
+ * compressed and saved in playback the image is decompressed
  */
 public class BitmapData implements Operation, SerializableToString {
     private static final int OP_CODE = Operations.DATA_BITMAP;
@@ -67,7 +67,6 @@
         return "BITMAP DATA " + mImageId;
     }
 
-
     public static String name() {
         return CLASS_NAME;
     }
@@ -84,7 +83,6 @@
         buffer.writeBuffer(bitmap);
     }
 
-
     public static void read(WireBuffer buffer, List<Operation> operations) {
         int imageId = buffer.readInt();
         int width = buffer.readInt();
@@ -99,19 +97,13 @@
         operations.add(new BitmapData(imageId, width, height, bitmap));
     }
 
-
     public static void documentation(DocumentationBuilder doc) {
-        doc.operation("Data Operations",
-                        OP_CODE,
-                        CLASS_NAME)
+        doc.operation("Data Operations", OP_CODE, CLASS_NAME)
                 .description("Bitmap data")
-                .field(INT, "id", "id of bitmap data")
-                .field(INT, "width",
-                        "width of the image")
-                .field(INT, "height",
-                        "height of the image")
-                .field(INT_ARRAY, "values", "length",
-                        "Array of ints");
+                .field(DocumentedOperation.INT, "id", "id of bitmap data")
+                .field(INT, "width", "width of the image")
+                .field(INT, "height", "height of the image")
+                .field(INT_ARRAY, "values", "length", "Array of ints");
     }
 
     @Override
@@ -124,9 +116,10 @@
         return indent + toString();
     }
 
+    @Override
     public void serializeToString(int indent, StringSerializer serializer) {
-        serializer.append(indent, CLASS_NAME
-                + " id " + mImageId + " (" + mImageWidth + "x" + mImageHeight + ")");
+        serializer.append(
+                indent,
+                CLASS_NAME + " id " + mImageId + " (" + mImageWidth + "x" + mImageHeight + ")");
     }
-
 }
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/ClickArea.java b/core/java/com/android/internal/widget/remotecompose/core/operations/ClickArea.java
index e72e24a..8b9e5a8 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/ClickArea.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/ClickArea.java
@@ -15,20 +15,17 @@
  */
 package com.android.internal.widget.remotecompose.core.operations;
 
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.FLOAT;
-
 import com.android.internal.widget.remotecompose.core.Operation;
 import com.android.internal.widget.remotecompose.core.Operations;
 import com.android.internal.widget.remotecompose.core.RemoteComposeOperation;
 import com.android.internal.widget.remotecompose.core.RemoteContext;
 import com.android.internal.widget.remotecompose.core.WireBuffer;
 import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
 
 import java.util.List;
 
-/**
- * Add a click area to the document
- */
+/** Add a click area to the document */
 public class ClickArea implements RemoteComposeOperation {
     private static final int OP_CODE = Operations.CLICK_AREA;
     private static final String CLASS_NAME = "ClickArea";
@@ -43,8 +40,8 @@
     /**
      * Add a click area to the document
      *
-     * @param id the id of the click area, which will be reported in the listener
-     *                           callback on the player
+     * @param id the id of the click area, which will be reported in the listener callback on the
+     *     player
      * @param contentDescription the content description (used for accessibility, as a textID)
      * @param left left coordinate of the area bounds
      * @param top top coordinate of the area bounds
@@ -52,10 +49,14 @@
      * @param bottom bottom coordinate of the area bounds
      * @param metadata associated metadata, user-provided (as a textID, pointing to a string)
      */
-    public ClickArea(int id, int contentDescription,
-                     float left, float top,
-                     float right, float bottom,
-                     int metadata) {
+    public ClickArea(
+            int id,
+            int contentDescription,
+            float left,
+            float top,
+            float right,
+            float bottom,
+            int metadata) {
         this.mId = id;
         this.mContentDescription = contentDescription;
         this.mLeft = left;
@@ -72,10 +73,27 @@
 
     @Override
     public String toString() {
-        return "CLICK_AREA <" + mId + " <" + mContentDescription + "> "
-                + "<" + mMetadata + ">+" + mLeft + " "
-                + mTop + " " + mRight + " " + mBottom + "+"
-                + " (" + (mRight - mLeft) + " x " + (mBottom - mTop) + " }";
+        return "CLICK_AREA <"
+                + mId
+                + " <"
+                + mContentDescription
+                + "> "
+                + "<"
+                + mMetadata
+                + ">+"
+                + mLeft
+                + " "
+                + mTop
+                + " "
+                + mRight
+                + " "
+                + mBottom
+                + "+"
+                + " ("
+                + (mRight - mLeft)
+                + " x "
+                + (mBottom - mTop)
+                + " }";
     }
 
     @Override
@@ -95,14 +113,19 @@
         return CLASS_NAME;
     }
 
-
     public static int id() {
         return OP_CODE;
     }
 
-    public static void apply(WireBuffer buffer, int id, int contentDescription,
-                             float left, float top, float right, float bottom,
-                             int metadata) {
+    public static void apply(
+            WireBuffer buffer,
+            int id,
+            int contentDescription,
+            float left,
+            float top,
+            float right,
+            float bottom,
+            int metadata) {
         buffer.start(OP_CODE);
         buffer.writeInt(id);
         buffer.writeInt(contentDescription);
@@ -113,7 +136,6 @@
         buffer.writeInt(metadata);
     }
 
-
     public static void read(WireBuffer buffer, List<Operation> operations) {
         int id = buffer.readInt();
         int contentDescription = buffer.readInt();
@@ -122,26 +144,21 @@
         float right = buffer.readFloat();
         float bottom = buffer.readFloat();
         int metadata = buffer.readInt();
-        ClickArea clickArea = new ClickArea(id, contentDescription,
-                left, top, right, bottom, metadata);
+        ClickArea clickArea =
+                new ClickArea(id, contentDescription, left, top, right, bottom, metadata);
         operations.add(clickArea);
     }
 
-
     public static void documentation(DocumentationBuilder doc) {
-        doc.operation("Canvas Operations",
-                        OP_CODE,
-                        CLASS_NAME)
+        doc.operation("Canvas Operations", OP_CODE, CLASS_NAME)
                 .description("Define a region you can click on")
-                .field(FLOAT, "left",
-                        "The left side of the region")
-                .field(FLOAT, "top",
-                        "The top of the region")
-                .field(FLOAT, "right",
-                        "The right side of the region")
-                .field(FLOAT, "bottom",
-                        "The bottom of the region")
-                .field(FLOAT, "metadata",
+                .field(DocumentedOperation.FLOAT, "left", "The left side of the region")
+                .field(DocumentedOperation.FLOAT, "top", "The top of the region")
+                .field(DocumentedOperation.FLOAT, "right", "The right side of the region")
+                .field(DocumentedOperation.FLOAT, "bottom", "The bottom of the region")
+                .field(
+                        DocumentedOperation.FLOAT,
+                        "metadata",
                         "user defined string accessible in callback");
     }
 }
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/ClipPath.java b/core/java/com/android/internal/widget/remotecompose/core/operations/ClipPath.java
index d77d53c..96b600a 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/ClipPath.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/ClipPath.java
@@ -15,21 +15,19 @@
  */
 package com.android.internal.widget.remotecompose.core.operations;
 
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
-
 import com.android.internal.widget.remotecompose.core.Operation;
 import com.android.internal.widget.remotecompose.core.Operations;
 import com.android.internal.widget.remotecompose.core.PaintContext;
 import com.android.internal.widget.remotecompose.core.PaintOperation;
 import com.android.internal.widget.remotecompose.core.WireBuffer;
 import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
 
 import java.util.List;
 
 /**
- * Defines a path that clips a the subsequent drawing commands
- * Use MatrixSave and MatrixRestore commands to remove clip
- * TODO allow id 0 to mean null?
+ * Defines a path that clips a the subsequent drawing commands Use MatrixSave and MatrixRestore
+ * commands to remove clip TODO allow id 0 to mean null?
  */
 public class ClipPath extends PaintOperation {
     private static final int OP_CODE = Operations.CLIP_PATH;
@@ -68,7 +66,6 @@
         return "ClipPath " + mId + ";";
     }
 
-
     public static void read(WireBuffer buffer, List<Operation> operations) {
         int pack = buffer.readInt();
         int id = pack & 0xFFFFF;
@@ -77,12 +74,10 @@
         operations.add(op);
     }
 
-
     public static String name() {
         return CLASS_NAME;
     }
 
-
     public static int id() {
         return OP_CODE;
     }
@@ -93,17 +88,13 @@
     }
 
     public static void documentation(DocumentationBuilder doc) {
-        doc.operation("Canvas Operations",
-                        OP_CODE,
-                        CLASS_NAME)
+        doc.operation("Canvas Operations", OP_CODE, CLASS_NAME)
                 .description("Intersect the current clip with the path")
-                .field(INT, "id",
-                        "id of the path");
+                .field(DocumentedOperation.INT, "id", "id of the path");
     }
 
-
     @Override
     public void paint(PaintContext context) {
         context.clipPath(mId, mRegionOp);
     }
-}
\ No newline at end of file
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/ClipRect.java b/core/java/com/android/internal/widget/remotecompose/core/operations/ClipRect.java
index ec9b6fe..b101bfb 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/ClipRect.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/ClipRect.java
@@ -15,24 +15,20 @@
  */
 package com.android.internal.widget.remotecompose.core.operations;
 
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.FLOAT;
-
 import com.android.internal.widget.remotecompose.core.Operation;
 import com.android.internal.widget.remotecompose.core.Operations;
 import com.android.internal.widget.remotecompose.core.PaintContext;
 import com.android.internal.widget.remotecompose.core.WireBuffer;
 import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
 
 import java.util.List;
 
-/**
- * Support clip with a rectangle
- */
+/** Support clip with a rectangle */
 public class ClipRect extends DrawBase4 {
     public static final int OP_CODE = Operations.CLIP_RECT;
     public static final String CLASS_NAME = "ClipRect";
 
-
     public static void read(WireBuffer buffer, List<Operation> operations) {
         Maker m = ClipRect::new;
         read(m, buffer, operations);
@@ -46,36 +42,33 @@
         return CLASS_NAME;
     }
 
-
-    protected void write(WireBuffer buffer,
-                         float v1,
-                         float v2,
-                         float v3,
-                         float v4) {
+    @Override
+    protected void write(WireBuffer buffer, float v1, float v2, float v3, float v4) {
         apply(buffer, v1, v2, v3, v4);
     }
 
     public static void documentation(DocumentationBuilder doc) {
-        doc.operation("Expressions Operations",
-                        OP_CODE,
-                        CLASS_NAME)
+        doc.operation("Expressions Operations", OP_CODE, CLASS_NAME)
                 .description("Intersect the current clip with rectangle")
-                .field(FLOAT, "left",
+                .field(
+                        DocumentedOperation.FLOAT,
+                        "left",
                         "The left side of the rectangle to intersect with the current clip")
-                .field(FLOAT, "top",
+                .field(
+                        DocumentedOperation.FLOAT,
+                        "top",
                         "The top of the rectangle to intersect with the current clip")
-                .field(FLOAT, "right",
+                .field(
+                        DocumentedOperation.FLOAT,
+                        "right",
                         "The right side of the rectangle to intersect with the current clip")
-                .field(FLOAT, "bottom",
+                .field(
+                        DocumentedOperation.FLOAT,
+                        "bottom",
                         "The bottom of the rectangle to intersect with the current clip");
     }
 
-
-    public ClipRect(
-            float left,
-            float top,
-            float right,
-            float bottom) {
+    public ClipRect(float left, float top, float right, float bottom) {
         super(left, top, right, bottom);
         mName = CLASS_NAME;
     }
@@ -89,16 +82,12 @@
      * Writes out the clipRect to the buffer
      *
      * @param buffer buffer to write to
-     * @param x1     start x of DrawOval
-     * @param y1     start y of the DrawOval
-     * @param x2     end x of the DrawOval
-     * @param y2     end y of the DrawOval
+     * @param x1 start x of DrawOval
+     * @param y1 start y of the DrawOval
+     * @param x2 end x of the DrawOval
+     * @param y2 end y of the DrawOval
      */
-    public static void apply(WireBuffer buffer,
-                             float x1,
-                             float y1,
-                             float x2,
-                             float y2) {
+    public static void apply(WireBuffer buffer, float x1, float y1, float x2, float y2) {
         write(buffer, OP_CODE, x1, y1, x2, y2);
     }
 }
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/ColorConstant.java b/core/java/com/android/internal/widget/remotecompose/core/operations/ColorConstant.java
index 2562e18..19d80da 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/ColorConstant.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/ColorConstant.java
@@ -15,20 +15,18 @@
  */
 package com.android.internal.widget.remotecompose.core.operations;
 
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
 
 import com.android.internal.widget.remotecompose.core.Operation;
 import com.android.internal.widget.remotecompose.core.Operations;
 import com.android.internal.widget.remotecompose.core.RemoteContext;
 import com.android.internal.widget.remotecompose.core.WireBuffer;
 import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
 
 import java.util.List;
 
-/**
- * Operation that defines a simple Color based on ID
- * Mainly for colors in theming.
- */
+/** Operation that defines a simple Color based on ID Mainly for colors in theming. */
 public class ColorConstant implements Operation {
     private static final int OP_CODE = Operations.COLOR_CONSTANT;
     private static final String CLASS_NAME = "ColorConstant";
@@ -78,14 +76,10 @@
     }
 
     public static void documentation(DocumentationBuilder doc) {
-        doc.operation("Expressions Operations",
-                        OP_CODE,
-                        CLASS_NAME)
+        doc.operation("Expressions Operations", OP_CODE, CLASS_NAME)
                 .description("Define a Color")
-                .field(INT, "id",
-                        "Id of the color")
-                .field(INT, "color",
-                        "32 bit ARGB color");
+                .field(DocumentedOperation.INT, "id", "Id of the color")
+                .field(INT, "color", "32 bit ARGB color");
     }
 
     @Override
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/ColorExpression.java b/core/java/com/android/internal/widget/remotecompose/core/operations/ColorExpression.java
index 96d6674..b6041ea 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/ColorExpression.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/ColorExpression.java
@@ -15,8 +15,8 @@
  */
 package com.android.internal.widget.remotecompose.core.operations;
 
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.FLOAT;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.FLOAT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
 
 import com.android.internal.widget.remotecompose.core.Operation;
 import com.android.internal.widget.remotecompose.core.Operations;
@@ -24,17 +24,13 @@
 import com.android.internal.widget.remotecompose.core.VariableSupport;
 import com.android.internal.widget.remotecompose.core.WireBuffer;
 import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
 
 import java.util.List;
 
 /**
- * Operation to Colors
- * Color modes
- * mMode = 0 two colors and a tween
- * mMode = 1 color1 is a colorID.
- * mMode = 2 color2 is a colorID.
- * mMode = 3 color1 & color2 are ids
- * mMode = 4  H S V mode
+ * Operation to Colors Color modes mMode = 0 two colors and a tween mMode = 1 color1 is a colorID.
+ * mMode = 2 color2 is a colorID. mMode = 3 color1 & color2 are ids mMode = 4 H S V mode
  */
 public class ColorExpression implements Operation, VariableSupport {
     private static final int OP_CODE = Operations.COLOR_EXPRESSIONS;
@@ -45,7 +41,6 @@
     public int mColor2;
     public float mTween = 0.0f;
 
-
     public float mHue = 0; // only in Mode 4
     public float mSat = 0;
     public float mValue = 0;
@@ -122,7 +117,6 @@
         }
     }
 
-
     @Override
     public void registerListening(RemoteContext context) {
         if (mMode == 4) {
@@ -151,8 +145,8 @@
     @Override
     public void apply(RemoteContext context) {
         if (mMode == 4) {
-            context.loadColor(mId, (mAlpha << 24)
-                    | (0xFFFFFF & Utils.hsvToRgb(mOutHue, mOutSat, mOutValue)));
+            context.loadColor(
+                    mId, (mAlpha << 24) | (0xFFFFFF & Utils.hsvToRgb(mOutHue, mOutSat, mOutValue)));
             return;
         }
         if (mOutTween == 0.0) {
@@ -165,8 +159,7 @@
                 mOutColor2 = context.getColor(mColor2);
             }
 
-            context.loadColor(mId,
-                    Utils.interpolateColor(mOutColor1, mOutColor2, mOutTween));
+            context.loadColor(mId, Utils.interpolateColor(mOutColor1, mOutColor2, mOutTween));
         }
     }
 
@@ -179,23 +172,34 @@
     @Override
     public String toString() {
         if (mMode == 4) {
-            return "ColorExpression[" + mId + "] = hsv (" + Utils.floatToString(mHue)
-                    + ", " + Utils.floatToString(mSat)
-                    + ", " + Utils.floatToString(mValue) + ")";
+            return "ColorExpression["
+                    + mId
+                    + "] = hsv ("
+                    + Utils.floatToString(mHue)
+                    + ", "
+                    + Utils.floatToString(mSat)
+                    + ", "
+                    + Utils.floatToString(mValue)
+                    + ")";
         }
 
         String c1 = (mMode & 1) == 1 ? "[" + mColor1 + "]" : Utils.colorInt(mColor1);
         String c2 = (mMode & 2) == 2 ? "[" + mColor2 + "]" : Utils.colorInt(mColor2);
-        return "ColorExpression[" + mId + "] = tween(" + c1
-                + ", " + c2 + ", "
-                + Utils.floatToString(mTween) + ")";
+        return "ColorExpression["
+                + mId
+                + "] = tween("
+                + c1
+                + ", "
+                + c2
+                + ", "
+                + Utils.floatToString(mTween)
+                + ")";
     }
 
     public static String name() {
         return CLASS_NAME;
     }
 
-
     public static int id() {
         return OP_CODE;
     }
@@ -204,22 +208,20 @@
      * Call to write a ColorExpression object on the buffer
      *
      * @param buffer
-     * @param id     of the ColorExpression object
-     * @param mode   if colors are id or actual values
+     * @param id of the ColorExpression object
+     * @param mode if colors are id or actual values
      * @param color1
      * @param color2
      * @param tween
      */
-    public static void apply(WireBuffer buffer,
-                             int id, int mode,
-                             int color1, int color2, float tween) {
+    public static void apply(
+            WireBuffer buffer, int id, int mode, int color1, int color2, float tween) {
         buffer.start(OP_CODE);
         buffer.writeInt(id);
         buffer.writeInt(mode);
         buffer.writeInt(color1);
         buffer.writeInt(color2);
         buffer.writeFloat(tween);
-
     }
 
     public static void read(WireBuffer buffer, List<Operation> operations) {
@@ -233,29 +235,22 @@
     }
 
     public static void documentation(DocumentationBuilder doc) {
-        doc.operation("Expressions Operations",
-                        OP_CODE,
-                        CLASS_NAME)
+        doc.operation("Expressions Operations", OP_CODE, CLASS_NAME)
                 .description("A Color defined by an expression")
-                .field(INT, "id", "Id of the color")
+                .field(DocumentedOperation.INT, "id", "Id of the color")
                 .field(INT, "mode", "The use of the next 3 fields")
                 .possibleValues("COLOR_COLOR_INTERPOLATE", 0)
                 .possibleValues("COLOR_ID_INTERPOLATE", 1)
                 .possibleValues("ID_COLOR_INTERPOLATE", 2)
                 .possibleValues("ID_ID_INTERPOLATE", 3)
                 .possibleValues("HSV", 4)
-                .field(INT, "color1",
-                        "32 bit ARGB color")
-                .field(INT, "color2",
-                        "32 bit ARGB color")
-                .field(FLOAT, "tween",
-                        "32 bit ARGB color");
-
+                .field(INT, "color1", "32 bit ARGB color")
+                .field(INT, "color2", "32 bit ARGB color")
+                .field(FLOAT, "tween", "32 bit ARGB color");
     }
 
     @Override
     public String deepToString(String indent) {
         return indent + toString();
     }
-
 }
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/ComponentValue.java b/core/java/com/android/internal/widget/remotecompose/core/operations/ComponentValue.java
index 22fe673..9929720 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/ComponentValue.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/ComponentValue.java
@@ -15,7 +15,7 @@
  */
 package com.android.internal.widget.remotecompose.core.operations;
 
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
 
 import com.android.internal.widget.remotecompose.core.Operation;
 import com.android.internal.widget.remotecompose.core.Operations;
@@ -23,6 +23,7 @@
 import com.android.internal.widget.remotecompose.core.SerializableToString;
 import com.android.internal.widget.remotecompose.core.WireBuffer;
 import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
 import com.android.internal.widget.remotecompose.core.operations.utilities.StringSerializer;
 
 import java.util.List;
@@ -82,15 +83,16 @@
     }
 
     public static void documentation(DocumentationBuilder doc) {
-        doc.operation("Expressions Operations",
-                        OP_CODE,
-                        CLASS_NAME)
+        doc.operation("Expressions Operations", OP_CODE, CLASS_NAME)
                 .description("Encode a component-related value (eg its width, height etc.)")
-                .field(INT, "TYPE",
+                .field(
+                        DocumentedOperation.INT,
+                        "TYPE",
                         "The type of value, either WIDTH(0) or HEIGHT(1)")
-                .field(INT, "COMPONENT_ID",
-                        "The component id to reference")
-                .field(INT, "VALUE_ID",
+                .field(INT, "COMPONENT_ID", "The component id to reference")
+                .field(
+                        INT,
+                        "VALUE_ID",
                         "The id of the RemoteFloat representing the described"
                                 + " component value, which can be used in expressions");
     }
@@ -105,14 +107,11 @@
      * Writes out the ComponentValue to the buffer
      *
      * @param buffer buffer to write to
-     * @param type   type of value (WIDTH or HEIGHT)
-     * @param componentId     component id to reference
-     * @param valueId  remote float used to represent the component value
+     * @param type type of value (WIDTH or HEIGHT)
+     * @param componentId component id to reference
+     * @param valueId remote float used to represent the component value
      */
-    public static void apply(WireBuffer buffer,
-                             int type,
-                             int componentId,
-                             int valueId) {
+    public static void apply(WireBuffer buffer, int type, int componentId, int valueId) {
         buffer.start(OP_CODE);
         buffer.writeInt(type);
         buffer.writeInt(componentId);
@@ -124,13 +123,20 @@
         return null;
     }
 
+    @Override
     public void serializeToString(int indent, StringSerializer serializer) {
         String type = "WIDTH";
         if (mType == HEIGHT) {
             type = "HEIGHT";
         }
-        serializer.append(indent, CLASS_NAME
-                + " value " + mValueId + " set to "
-                + type + " of Component " + mComponentID);
+        serializer.append(
+                indent,
+                CLASS_NAME
+                        + " value "
+                        + mValueId
+                        + " set to "
+                        + type
+                        + " of Component "
+                        + mComponentID);
     }
 }
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DataListFloat.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DataListFloat.java
index edcb5fe..0075869 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DataListFloat.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DataListFloat.java
@@ -15,8 +15,8 @@
  */
 package com.android.internal.widget.remotecompose.core.operations;
 
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.FLOAT_ARRAY;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.FLOAT_ARRAY;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
 
 import com.android.internal.widget.remotecompose.core.Operation;
 import com.android.internal.widget.remotecompose.core.Operations;
@@ -24,6 +24,7 @@
 import com.android.internal.widget.remotecompose.core.VariableSupport;
 import com.android.internal.widget.remotecompose.core.WireBuffer;
 import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
 import com.android.internal.widget.remotecompose.core.operations.utilities.ArrayAccess;
 
 import java.util.Arrays;
@@ -63,7 +64,7 @@
 
     @Override
     public String toString() {
-        return "DataListFloat[A_" + (mId & 0xFFFF) + "] " + Arrays.toString(mValues);
+        return "DataListFloat[" + Utils.idString(mId) + "] " + Arrays.toString(mValues);
     }
 
     public static void apply(WireBuffer buffer, int id, float[] values) {
@@ -90,14 +91,11 @@
     }
 
     public static void documentation(DocumentationBuilder doc) {
-        doc.operation("Data Operations",
-                        OP_CODE,
-                        CLASS_NAME)
+        doc.operation("Data Operations", OP_CODE, CLASS_NAME)
                 .description("a list of Floats")
-                .field(INT, "id", "id the array (2xxxxx)")
+                .field(DocumentedOperation.INT, "id", "id the array (2xxxxx)")
                 .field(INT, "length", "number of floats")
-                .field(FLOAT_ARRAY, "values", "length",
-                        "array of floats");
+                .field(FLOAT_ARRAY, "values", "length", "array of floats");
     }
 
     @Override
@@ -121,7 +119,7 @@
     }
 
     @Override
-    public int getFloatsLength() {
+    public int getLength() {
         return mValues.length;
     }
 }
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DataListIds.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DataListIds.java
index bde376e..c43dab4 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DataListIds.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DataListIds.java
@@ -15,8 +15,8 @@
  */
 package com.android.internal.widget.remotecompose.core.operations;
 
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT_ARRAY;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT_ARRAY;
 
 import com.android.internal.widget.remotecompose.core.Operation;
 import com.android.internal.widget.remotecompose.core.Operations;
@@ -24,39 +24,29 @@
 import com.android.internal.widget.remotecompose.core.VariableSupport;
 import com.android.internal.widget.remotecompose.core.WireBuffer;
 import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
 import com.android.internal.widget.remotecompose.core.operations.utilities.ArrayAccess;
 
 import java.util.Arrays;
 import java.util.List;
 
-public class DataListIds implements VariableSupport, ArrayAccess, Operation  {
+public class DataListIds implements VariableSupport, ArrayAccess, Operation {
     private static final int OP_CODE = Operations.ID_LIST;
     private static final String CLASS_NAME = "IdListData";
     int mId;
     int[] mIds;
-    float[] mValues;
     private static final int MAX_LIST = 2000;
 
     public DataListIds(int id, int[] ids) {
         mId = id;
         mIds = ids;
-        mValues = new float[ids.length];
     }
 
     @Override
-    public void updateVariables(RemoteContext context) {
-        for (int i = 0; i < mIds.length; i++) {
-            int id = mIds[i];
-            mValues[i] = context.getFloat(id);
-        }
-    }
+    public void updateVariables(RemoteContext context) {}
 
     @Override
-    public void registerListening(RemoteContext context) {
-        for (int mId : mIds) {
-            context.listensTo(mId, this);
-        }
-    }
+    public void registerListening(RemoteContext context) {}
 
     @Override
     public void write(WireBuffer buffer) {
@@ -65,7 +55,7 @@
 
     @Override
     public String toString() {
-        return "map " + "\"" + Arrays.toString(mIds) + "\"";
+        return "map[" + Utils.idString(mId) + "]  \"" + Arrays.toString(mIds) + "\"";
     }
 
     public static void apply(WireBuffer buffer, int id, int[] ids) {
@@ -92,15 +82,11 @@
     }
 
     public static void documentation(DocumentationBuilder doc) {
-        doc.operation("Data Operations",
-                        OP_CODE,
-                        CLASS_NAME)
+        doc.operation("Data Operations", OP_CODE, CLASS_NAME)
                 .description("a list of id's")
-                .field(INT, "id", "id the array")
+                .field(DocumentedOperation.INT, "id", "id the array")
                 .field(INT, "length", "number of ids")
-                .field(INT_ARRAY, "ids[n]", "length",
-                        "ids of other variables");
-
+                .field(INT_ARRAY, "ids[n]", "length", "ids of other variables");
     }
 
     @Override
@@ -115,16 +101,26 @@
 
     @Override
     public float getFloatValue(int index) {
-        return mValues[index];
+        return Float.NaN;
+    }
+
+    @Override
+    public int getId(int index) {
+        return mIds[index];
     }
 
     @Override
     public float[] getFloats() {
-        return mValues;
+        return null;
     }
 
     @Override
-    public int getFloatsLength() {
-        return mValues.length;
+    public int getLength() {
+        return mIds.length;
+    }
+
+    @Override
+    public int getIntValue(int index) {
+        return 0;
     }
 }
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DataMapIds.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DataMapIds.java
index 53143dc..75db29d 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DataMapIds.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DataMapIds.java
@@ -15,78 +15,82 @@
  */
 package com.android.internal.widget.remotecompose.core.operations;
 
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.UTF8;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.UTF8;
 
 import com.android.internal.widget.remotecompose.core.Operation;
 import com.android.internal.widget.remotecompose.core.Operations;
 import com.android.internal.widget.remotecompose.core.RemoteContext;
-import com.android.internal.widget.remotecompose.core.VariableSupport;
 import com.android.internal.widget.remotecompose.core.WireBuffer;
 import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
-import com.android.internal.widget.remotecompose.core.operations.utilities.ArrayAccess;
+import com.android.internal.widget.remotecompose.core.operations.utilities.DataMap;
 
 import java.util.List;
 
-public class DataMapIds implements VariableSupport, ArrayAccess, Operation  {
+/** This is a map of strings to type & Id */
+public class DataMapIds implements Operation {
     private static final int OP_CODE = Operations.ID_MAP;
-    private static final String CLASS_NAME = "IdMapData";
+    private static final String CLASS_NAME = "DataMapIds";
     int mId;
-    String[] mNames;
-    int[] mIds;
-    float[] mValues;
+    DataMap mDataMap;
+
     private static final int MAX_MAP = 2000;
 
-    public DataMapIds(int id, String[] names, int[] ids) {
+    public static final byte TYPE_STRING = 0;
+    public static final byte TYPE_INT = 1;
+    public static final byte TYPE_FLOAT = 2;
+    public static final byte TYPE_LONG = 3;
+    public static final byte TYPE_BOOLEAN = 4;
+
+    private String typeString(byte type) {
+        switch (type) {
+            case TYPE_STRING:
+                return "String";
+            case TYPE_INT:
+                return "Int";
+            case TYPE_FLOAT:
+                return "Float";
+            case TYPE_LONG:
+                return "Long";
+            case TYPE_BOOLEAN:
+                return "Boolean";
+        }
+        return "?";
+    }
+
+    public DataMapIds(int id, String[] names, byte[] types, int[] ids) {
         mId = id;
-        mNames = names;
-        mIds = ids;
-        mValues = new float[ids.length];
-
-    }
-
-    @Override
-    public void updateVariables(RemoteContext context) {
-        for (int i = 0; i < mIds.length; i++) {
-            int id = mIds[i];
-            mValues[i] = context.getFloat(id);
-        }
-    }
-
-    @Override
-    public void registerListening(RemoteContext context) {
-        for (int mId : mIds) {
-            context.listensTo(mId, this);
-        }
+        mDataMap = new DataMap(names, types, ids);
     }
 
     @Override
     public void write(WireBuffer buffer) {
-        apply(buffer, mId, mNames, mIds);
+        apply(buffer, mId, mDataMap.mNames, mDataMap.mTypes, mDataMap.mIds);
     }
 
     @Override
     public String toString() {
-        StringBuilder builder = new StringBuilder("DataMapIds ");
-        for (int i = 0; i < mNames.length; i++) {
+        StringBuilder builder = new StringBuilder("DataMapIds[" + Utils.idString(mId) + "] ");
+        for (int i = 0; i < mDataMap.mNames.length; i++) {
             if (i != 0) {
                 builder.append(" ");
             }
-            builder.append(mNames[i]);
+            builder.append(typeString(mDataMap.mTypes[i]));
             builder.append("[");
-            builder.append(mIds[i]);
-            builder.append("]");
-
+            builder.append(mDataMap.mNames[i]);
+            builder.append("]=");
+            builder.append(mDataMap.mIds[i]);
         }
         return builder.toString();
     }
 
-    public static void apply(WireBuffer buffer, int id, String[] names, int[] ids) {
+    public static void apply(WireBuffer buffer, int id, String[] names, byte[] type, int[] ids) {
         buffer.start(OP_CODE);
         buffer.writeInt(id);
         buffer.writeInt(names.length);
         for (int i = 0; i < names.length; i++) {
             buffer.writeUTF8(names[i]);
+            buffer.writeByte(type == null ? 2 : type[i]);
             buffer.writeInt(ids[i]);
         }
     }
@@ -99,25 +103,23 @@
         }
         String[] names = new String[len];
         int[] ids = new int[len];
+        byte[] types = new byte[len];
         for (int i = 0; i < names.length; i++) {
             names[i] = buffer.readUTF8();
+            types[i] = (byte) buffer.readByte();
             ids[i] = buffer.readInt();
         }
-        DataMapIds data = new DataMapIds(id, names, ids);
+        DataMapIds data = new DataMapIds(id, names, types, ids);
         operations.add(data);
     }
 
     public static void documentation(DocumentationBuilder doc) {
-        doc.operation("Data Operations",
-                        OP_CODE,
-                        CLASS_NAME)
+        doc.operation("Data Operations", OP_CODE, CLASS_NAME)
                 .description("Encode a collection of name id pairs")
                 .field(INT, "id", "id the array")
                 .field(INT, "length", "number of entries")
-                .field(INT, "names[0]", "length",
-                        "path encoded as floats")
-                .field(UTF8, "id[0]", "length",
-                        "path encoded as floats");
+                .field(INT, "names[0]", "length", "path encoded as floats")
+                .field(UTF8, "id[0]", "length", "path encoded as floats");
     }
 
     @Override
@@ -127,21 +129,6 @@
 
     @Override
     public void apply(RemoteContext context) {
-        context.addCollection(mId, this);
-    }
-
-    @Override
-    public float getFloatValue(int index) {
-        return mValues[index];
-    }
-
-    @Override
-    public float[] getFloats() {
-        return mValues;
-    }
-
-    @Override
-    public int getFloatsLength() {
-        return mValues.length;
+        context.putDataMap(mId, mDataMap);
     }
 }
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DataMapLookup.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DataMapLookup.java
new file mode 100644
index 0000000..fb5e5d1
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DataMapLookup.java
@@ -0,0 +1,148 @@
+/*
+ * 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.widget.remotecompose.core.operations;
+
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
+
+import com.android.internal.widget.remotecompose.core.Operation;
+import com.android.internal.widget.remotecompose.core.Operations;
+import com.android.internal.widget.remotecompose.core.RemoteContext;
+import com.android.internal.widget.remotecompose.core.WireBuffer;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.operations.utilities.DataMap;
+import com.android.internal.widget.remotecompose.core.types.BooleanConstant;
+import com.android.internal.widget.remotecompose.core.types.LongConstant;
+
+import java.util.List;
+
+/** This can lookup in a map given a string writing the results to an id. */
+public class DataMapLookup implements Operation {
+    private static final int OP_CODE = Operations.DATA_MAP_LOOKUP;
+    private static final String CLASS_NAME = "DataMapLookup";
+    public int mId;
+    public int mDataMapId;
+    public int mStringId;
+
+    /**
+     * Create an access to a data map
+     *
+     * @param id of the output value
+     * @param dataMapId the id of the data map
+     * @param keyStringId the string to be looked up
+     */
+    public DataMapLookup(int id, int dataMapId, int keyStringId) {
+        this.mId = id;
+        this.mDataMapId = dataMapId;
+        this.mStringId = keyStringId;
+    }
+
+    @Override
+    public void write(WireBuffer buffer) {
+        apply(buffer, mId, mDataMapId, mStringId);
+    }
+
+    @Override
+    public String toString() {
+        return "DataMapLookup[" + mId + "] = " + Utils.idString(mDataMapId) + " " + mStringId;
+    }
+
+    /**
+     * The class name
+     *
+     * @return the name of the class
+     */
+    public static String name() {
+        return CLASS_NAME;
+    }
+
+    /**
+     * The opcode
+     *
+     * @return the opcode
+     */
+    public static int id() {
+        return OP_CODE;
+    }
+
+    /**
+     * Writes out the operation to the buffer
+     *
+     * @param buffer write command to this buffer
+     * @param id the id
+     * @param dataMapId the map to extract from
+     * @param keyStringId the map to extract from
+     */
+    public static void apply(WireBuffer buffer, int id, int dataMapId, int keyStringId) {
+        buffer.start(OP_CODE);
+        buffer.writeInt(id);
+        buffer.writeInt(dataMapId);
+        buffer.writeInt(keyStringId);
+    }
+
+    /**
+     * The read the buffer and create the command
+     *
+     * @param buffer buffer
+     * @param operations the created command is added to the list
+     */
+    public static void read(WireBuffer buffer, List<Operation> operations) {
+        int id = buffer.readInt();
+        int mapId = buffer.readInt();
+        int stringId = buffer.readInt();
+        operations.add(new DataMapLookup(id, mapId, stringId));
+    }
+
+    public static void documentation(DocumentationBuilder doc) {
+        doc.operation("Expressions Operations", OP_CODE, CLASS_NAME)
+                .description("A float and its associated id")
+                .field(INT, "id", "id of float")
+                .field(INT, "dataMapId", "32-bit float value")
+                .field(INT, "value", "32-bit float value");
+    }
+
+    @Override
+    public void apply(RemoteContext context) {
+        String str = context.getText(mStringId);
+        DataMap data = context.getDataMap(mDataMapId);
+        int pos = data.getPos(str);
+        byte type = data.getType(pos);
+        int dataId = data.getId(pos);
+        switch (type) {
+            case DataMapIds.TYPE_STRING:
+                context.loadText(mId, context.getText(dataId));
+                break;
+            case DataMapIds.TYPE_INT:
+                context.loadInteger(mId, context.getInteger(dataId));
+                break;
+            case DataMapIds.TYPE_FLOAT:
+                context.loadFloat(mId, context.getFloat(dataId));
+                break;
+            case DataMapIds.TYPE_LONG:
+                LongConstant lc = (LongConstant) context.getObject(dataId);
+                context.loadInteger(mId, (int) lc.getValue());
+                break;
+            case DataMapIds.TYPE_BOOLEAN:
+                BooleanConstant bc = (BooleanConstant) context.getObject(dataId);
+                context.loadInteger(mId, bc.getValue() ? 1 : 0);
+                break;
+        }
+    }
+
+    @Override
+    public String deepToString(String indent) {
+        return indent + toString();
+    }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawArc.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawArc.java
index d377229..e078307 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawArc.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawArc.java
@@ -15,13 +15,12 @@
  */
 package com.android.internal.widget.remotecompose.core.operations;
 
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.FLOAT;
-
 import com.android.internal.widget.remotecompose.core.Operation;
 import com.android.internal.widget.remotecompose.core.Operations;
 import com.android.internal.widget.remotecompose.core.PaintContext;
 import com.android.internal.widget.remotecompose.core.WireBuffer;
 import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
 
 import java.util.List;
 
@@ -40,6 +39,7 @@
 
     /**
      * Writes out the operation to the buffer
+     *
      * @param buffer the buffer to write to
      * @param v1 The left side of the Oval
      * @param v2 The top of the Oval
@@ -48,13 +48,8 @@
      * @param v5 Starting angle (in degrees) where the arc begins
      * @param v6 Sweep angle (in degrees) measured clockwise
      */
-    public static void apply(WireBuffer buffer,
-                             float v1,
-                             float v2,
-                             float v3,
-                             float v4,
-                             float v5,
-                             float v6) {
+    public static void apply(
+            WireBuffer buffer, float v1, float v2, float v3, float v4, float v5, float v6) {
         buffer.start(OP_CODE);
         buffer.writeFloat(v1);
         buffer.writeFloat(v2);
@@ -64,47 +59,37 @@
         buffer.writeFloat(v6);
     }
 
-    protected void write(WireBuffer buffer,
-                             float v1,
-                             float v2,
-                             float v3,
-                             float v4,
-                             float v5,
-                             float v6) {
+    @Override
+    protected void write(
+            WireBuffer buffer, float v1, float v2, float v3, float v4, float v5, float v6) {
         apply(buffer, v1, v2, v3, v4, v5, v6);
     }
 
     public static void documentation(DocumentationBuilder doc) {
-        doc.operation("Canvas Operations",
-                        OP_CODE,
-                        CLASS_NAME)
-                .description("Draw the specified arc"
-                        + "which will be scaled to fit inside the specified oval")
-                .field(FLOAT, "left",
-                        "The left side of the Oval")
-                .field(FLOAT, "top",
-                        "The top of the Oval")
-                .field(FLOAT, "right",
-                        "The right side of the Oval")
-                .field(FLOAT, "bottom",
-                        "The bottom of the Oval")
-                .field(FLOAT, "startAngle",
+        doc.operation("Canvas Operations", OP_CODE, CLASS_NAME)
+                .description(
+                        "Draw the specified arc"
+                                + "which will be scaled to fit inside the specified oval")
+                .field(DocumentedOperation.FLOAT, "left", "The left side of the Oval")
+                .field(DocumentedOperation.FLOAT, "top", "The top of the Oval")
+                .field(DocumentedOperation.FLOAT, "right", "The right side of the Oval")
+                .field(DocumentedOperation.FLOAT, "bottom", "The bottom of the Oval")
+                .field(
+                        DocumentedOperation.FLOAT,
+                        "startAngle",
                         "Starting angle (in degrees) where the arc begins")
-                .field(FLOAT, "sweepAngle",
+                .field(
+                        DocumentedOperation.FLOAT,
+                        "sweepAngle",
                         "Sweep angle (in degrees) measured clockwise");
     }
 
-
-    public DrawArc(float v1,
-                   float v2,
-                   float v3,
-                   float v4,
-                   float v5,
-                   float v6) {
+    public DrawArc(float v1, float v2, float v3, float v4, float v5, float v6) {
         super(v1, v2, v3, v4, v5, v6);
         mName = "DrawArc";
     }
 
+    @Override
     public void paint(PaintContext context) {
         context.drawArc(mV1, mV2, mV3, mV4, mV5, mV6);
     }
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase2.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase2.java
index 97eb76b..c678cc4 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase2.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase2.java
@@ -25,11 +25,8 @@
 
 import java.util.List;
 
-/**
- * Base class for commands that take 3 float
- */
-public abstract class DrawBase2 extends PaintOperation
-        implements VariableSupport {
+/** Base class for commands that take 3 float */
+public abstract class DrawBase2 extends PaintOperation implements VariableSupport {
     protected String mName = "DrawRectBase";
     float mV1;
     float mV2;
@@ -45,10 +42,8 @@
 
     @Override
     public void updateVariables(RemoteContext context) {
-        mV1 = (Float.isNaN(mValue1))
-                ? context.getFloat(Utils.idFromNan(mValue1)) : mValue1;
-        mV2 = (Float.isNaN(mValue2))
-                ? context.getFloat(Utils.idFromNan(mValue2)) : mValue2;
+        mV1 = Float.isNaN(mValue1) ? context.getFloat(Utils.idFromNan(mValue1)) : mValue1;
+        mV2 = Float.isNaN(mValue2) ? context.getFloat(Utils.idFromNan(mValue2)) : mValue2;
     }
 
     @Override
@@ -61,7 +56,6 @@
         }
     }
 
-
     @Override
     public void write(WireBuffer buffer) {
         write(buffer, mV1, mV2);
@@ -70,8 +64,7 @@
     protected abstract void write(WireBuffer buffer, float v1, float v2);
 
     protected interface Maker {
-        DrawBase2 create(float v1,
-                         float v2);
+        DrawBase2 create(float v1, float v2);
     }
 
     @Override
@@ -79,7 +72,6 @@
         return mName + " " + floatToString(mV1) + " " + floatToString(mV2);
     }
 
-
     public static void read(Maker maker, WireBuffer buffer, List<Operation> operations) {
         float v1 = buffer.readFloat();
         float v2 = buffer.readFloat();
@@ -99,7 +91,6 @@
         return null;
     }
 
-
     /**
      * Writes out the operation to the buffer
      *
@@ -108,13 +99,9 @@
      * @param x1
      * @param y1
      */
-    protected static void write(WireBuffer buffer,
-                                int opCode,
-                                float x1,
-                                float y1) {
+    protected static void write(WireBuffer buffer, int opCode, float x1, float y1) {
         buffer.start(opCode);
         buffer.writeFloat(x1);
         buffer.writeFloat(y1);
-
     }
 }
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase3.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase3.java
index 2d1d3eb..e1108e9 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase3.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase3.java
@@ -25,11 +25,8 @@
 
 import java.util.List;
 
-/**
- * Base class for commands that take 3 float
- */
-public abstract class DrawBase3 extends PaintOperation
-        implements VariableSupport {
+/** Base class for commands that take 3 float */
+public abstract class DrawBase3 extends PaintOperation implements VariableSupport {
 
     protected String mName = "DrawRectBase";
     float mV1;
@@ -39,10 +36,7 @@
     float mValue2;
     float mValue3;
 
-    public DrawBase3(
-            float v1,
-            float v2,
-            float v3) {
+    public DrawBase3(float v1, float v2, float v3) {
         mValue1 = v1;
         mValue2 = v2;
         mValue3 = v3;
@@ -54,12 +48,9 @@
 
     @Override
     public void updateVariables(RemoteContext context) {
-        mV1 = (Utils.isVariable(mValue1))
-                ? context.getFloat(Utils.idFromNan(mValue1)) : mValue1;
-        mV2 = (Utils.isVariable(mValue2))
-                ? context.getFloat(Utils.idFromNan(mValue2)) : mValue2;
-        mV3 = (Utils.isVariable(mValue3))
-                ? context.getFloat(Utils.idFromNan(mValue3)) : mValue3;
+        mV1 = Utils.isVariable(mValue1) ? context.getFloat(Utils.idFromNan(mValue1)) : mValue1;
+        mV2 = Utils.isVariable(mValue2) ? context.getFloat(Utils.idFromNan(mValue2)) : mValue2;
+        mV3 = Utils.isVariable(mValue3) ? context.getFloat(Utils.idFromNan(mValue3)) : mValue3;
     }
 
     @Override
@@ -80,21 +71,21 @@
         write(buffer, mV1, mV2, mV3);
     }
 
-    protected abstract void write(WireBuffer buffer,
-                                  float v1,
-                                  float v2,
-                                  float v3);
+    protected abstract void write(WireBuffer buffer, float v1, float v2, float v3);
 
     interface Maker {
-        DrawBase3 create(float v1,
-                         float v2,
-                         float v3);
+        DrawBase3 create(float v1, float v2, float v3);
     }
 
     @Override
     public String toString() {
-        return mName + " " + floatToString(mV1) + " " + floatToString(mV2)
-                + " " + floatToString(mV3);
+        return mName
+                + " "
+                + floatToString(mV1)
+                + " "
+                + floatToString(mV2)
+                + " "
+                + floatToString(mV3);
     }
 
     public static void read(Maker maker, WireBuffer buffer, List<Operation> operations) {
@@ -106,17 +97,14 @@
     }
 
     /**
-     * Construct and Operation from the 3 variables.
-     * This must be overridden by subclasses
+     * Construct and Operation from the 3 variables. This must be overridden by subclasses
      *
      * @param x1
      * @param y1
      * @param x2
      * @return
      */
-    public Operation construct(float x1,
-                               float y1,
-                               float x2) {
+    public Operation construct(float x1, float y1, float x2) {
         return null;
     }
 }
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase4.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase4.java
index 943c5a4..09f0df9 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase4.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase4.java
@@ -25,11 +25,8 @@
 
 import java.util.List;
 
-/**
- * Base class for draw commands that take 4 floats
- */
-public abstract class DrawBase4 extends PaintOperation
-        implements VariableSupport {
+/** Base class for draw commands that take 4 floats */
+public abstract class DrawBase4 extends PaintOperation implements VariableSupport {
     protected String mName = "DrawRectBase";
     protected float mX1;
     protected float mY1;
@@ -40,11 +37,7 @@
     float mX2Value;
     float mY2Value;
 
-    public DrawBase4(
-            float x1,
-            float y1,
-            float x2,
-            float y2) {
+    public DrawBase4(float x1, float y1, float x2, float y2) {
         mX1Value = x1;
         mY1Value = y1;
         mX2Value = x2;
@@ -58,14 +51,10 @@
 
     @Override
     public void updateVariables(RemoteContext context) {
-        mX1 = (Float.isNaN(mX1Value))
-                ? context.getFloat(Utils.idFromNan(mX1Value)) : mX1Value;
-        mY1 = (Float.isNaN(mY1Value))
-                ? context.getFloat(Utils.idFromNan(mY1Value)) : mY1Value;
-        mX2 = (Float.isNaN(mX2Value))
-                ? context.getFloat(Utils.idFromNan(mX2Value)) : mX2Value;
-        mY2 = (Float.isNaN(mY2Value))
-                ? context.getFloat(Utils.idFromNan(mY2Value)) : mY2Value;
+        mX1 = Float.isNaN(mX1Value) ? context.getFloat(Utils.idFromNan(mX1Value)) : mX1Value;
+        mY1 = Float.isNaN(mY1Value) ? context.getFloat(Utils.idFromNan(mY1Value)) : mY1Value;
+        mX2 = Float.isNaN(mX2Value) ? context.getFloat(Utils.idFromNan(mX2Value)) : mX2Value;
+        mY2 = Float.isNaN(mY2Value) ? context.getFloat(Utils.idFromNan(mY2Value)) : mY2Value;
     }
 
     @Override
@@ -89,23 +78,23 @@
         write(buffer, mX1, mY1, mX2, mY2);
     }
 
-    protected abstract void write(WireBuffer buffer,
-                                  float v1,
-                                  float v2,
-                                  float v3,
-                                  float v4);
+    protected abstract void write(WireBuffer buffer, float v1, float v2, float v3, float v4);
 
     protected interface Maker {
-        DrawBase4 create(float v1,
-                         float v2,
-                         float v3,
-                         float v4);
+        DrawBase4 create(float v1, float v2, float v3, float v4);
     }
 
     @Override
     public String toString() {
-        return mName + " " + floatToString(mX1Value, mX1) + " " + floatToString(mY1Value, mY1)
-                + " " + floatToString(mX2Value, mX2) + " " + floatToString(mY2Value, mY2);
+        return mName
+                + " "
+                + floatToString(mX1Value, mX1)
+                + " "
+                + floatToString(mY1Value, mY1)
+                + " "
+                + floatToString(mX2Value, mX2)
+                + " "
+                + floatToString(mY2Value, mY2);
     }
 
     public static void read(Maker maker, WireBuffer buffer, List<Operation> operations) {
@@ -127,14 +116,10 @@
      * @param y2
      * @return
      */
-    public Operation construct(float x1,
-                               float y1,
-                               float x2,
-                               float y2) {
+    public Operation construct(float x1, float y1, float x2, float y2) {
         return null;
     }
 
-
     /**
      * Writes out the operation to the buffer
      *
@@ -145,12 +130,8 @@
      * @param x2
      * @param y2
      */
-    protected static void write(WireBuffer buffer,
-                                int opCode,
-                                float x1,
-                                float y1,
-                                float x2,
-                                float y2) {
+    protected static void write(
+            WireBuffer buffer, int opCode, float x1, float y1, float x2, float y2) {
         buffer.start(opCode);
         buffer.writeFloat(x1);
         buffer.writeFloat(y1);
@@ -158,4 +139,3 @@
         buffer.writeFloat(y2);
     }
 }
-
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase6.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase6.java
index 767a36d..e071d5f 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase6.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase6.java
@@ -15,8 +15,6 @@
  */
 package com.android.internal.widget.remotecompose.core.operations;
 
-import static com.android.internal.widget.remotecompose.core.operations.Utils.floatToString;
-
 import com.android.internal.widget.remotecompose.core.Operation;
 import com.android.internal.widget.remotecompose.core.PaintOperation;
 import com.android.internal.widget.remotecompose.core.RemoteContext;
@@ -25,11 +23,8 @@
 
 import java.util.List;
 
-/**
- * Base class for draw commands the take 6 floats
- */
-public abstract class DrawBase6 extends PaintOperation
-        implements VariableSupport {
+/** Base class for draw commands the take 6 floats */
+public abstract class DrawBase6 extends PaintOperation implements VariableSupport {
     protected String mName = "DrawRectBase";
     float mV1;
     float mV2;
@@ -44,13 +39,7 @@
     float mValue5;
     float mValue6;
 
-    public DrawBase6(
-            float v1,
-            float v2,
-            float v3,
-            float v4,
-            float v5,
-            float v6) {
+    public DrawBase6(float v1, float v2, float v3, float v4, float v5, float v6) {
         mValue1 = v1;
         mValue2 = v2;
         mValue3 = v3;
@@ -68,18 +57,12 @@
 
     @Override
     public void updateVariables(RemoteContext context) {
-        mV1 = (Float.isNaN(mValue1))
-                ? context.getFloat(Utils.idFromNan(mValue1)) : mValue1;
-        mV2 = (Float.isNaN(mValue2))
-                ? context.getFloat(Utils.idFromNan(mValue2)) : mValue2;
-        mV3 = (Float.isNaN(mValue3))
-                ? context.getFloat(Utils.idFromNan(mValue3)) : mValue3;
-        mV4 = (Float.isNaN(mValue4))
-                ? context.getFloat(Utils.idFromNan(mValue4)) : mValue4;
-        mV5 = (Float.isNaN(mValue5))
-                ? context.getFloat(Utils.idFromNan(mValue5)) : mValue5;
-        mV6 = (Float.isNaN(mValue6))
-                ? context.getFloat(Utils.idFromNan(mValue6)) : mValue6;
+        mV1 = Float.isNaN(mValue1) ? context.getFloat(Utils.idFromNan(mValue1)) : mValue1;
+        mV2 = Float.isNaN(mValue2) ? context.getFloat(Utils.idFromNan(mValue2)) : mValue2;
+        mV3 = Float.isNaN(mValue3) ? context.getFloat(Utils.idFromNan(mValue3)) : mValue3;
+        mV4 = Float.isNaN(mValue4) ? context.getFloat(Utils.idFromNan(mValue4)) : mValue4;
+        mV5 = Float.isNaN(mValue5) ? context.getFloat(Utils.idFromNan(mValue5)) : mValue5;
+        mV6 = Float.isNaN(mValue6) ? context.getFloat(Utils.idFromNan(mValue6)) : mValue6;
     }
 
     @Override
@@ -109,27 +92,24 @@
         write(buffer, mV1, mV2, mV3, mV4, mV5, mV6);
     }
 
-    protected abstract void write(WireBuffer buffer,
-                                  float v1,
-                                  float v2,
-                                  float v3,
-                                  float v4,
-                                  float v5,
-                                  float v6);
+    protected abstract void write(
+            WireBuffer buffer, float v1, float v2, float v3, float v4, float v5, float v6);
 
     @Override
     public String toString() {
-        return mName + " " + floatToString(mV1) + " " + floatToString(mV2)
-                + " " + floatToString(mV3) + " " + floatToString(mV4);
+        return mName
+                + " "
+                + Utils.floatToString(mV1)
+                + " "
+                + Utils.floatToString(mV2)
+                + " "
+                + Utils.floatToString(mV3)
+                + " "
+                + Utils.floatToString(mV4);
     }
 
     interface Maker {
-        DrawBase6 create(float v1,
-                         float v2,
-                         float v3,
-                         float v4,
-                         float v5,
-                         float v6);
+        DrawBase6 create(float v1, float v2, float v3, float v4, float v5, float v6);
     }
 
     public static void read(Maker build, WireBuffer buffer, List<Operation> operations) {
@@ -155,18 +135,11 @@
      * @param v6
      * @return
      */
-    public Operation construct(float v1,
-                               float v2,
-                               float v3,
-                               float v4,
-                               float v5,
-                               float v6) {
+    public Operation construct(float v1, float v2, float v3, float v4, float v5, float v6) {
         return null;
     }
 
-
     public static String name() {
         return "DrawBase6";
     }
-
 }
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBitmap.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBitmap.java
index 2748f4c..0b43fd2 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBitmap.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBitmap.java
@@ -15,8 +15,8 @@
  */
 package com.android.internal.widget.remotecompose.core.operations;
 
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.FLOAT;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.FLOAT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
 
 import com.android.internal.widget.remotecompose.core.Operation;
 import com.android.internal.widget.remotecompose.core.Operations;
@@ -44,12 +44,7 @@
     int mDescriptionId = 0;
 
     public DrawBitmap(
-            int imageId,
-            float left,
-            float top,
-            float right,
-            float bottom,
-            int descriptionId) {
+            int imageId, float left, float top, float right, float bottom, int descriptionId) {
         mLeft = left;
         mTop = top;
         mRight = right;
@@ -60,14 +55,10 @@
 
     @Override
     public void updateVariables(RemoteContext context) {
-        mOutputLeft = (Float.isNaN(mLeft))
-                ? context.getFloat(Utils.idFromNan(mLeft)) : mLeft;
-        mOutputTop = (Float.isNaN(mTop))
-                ? context.getFloat(Utils.idFromNan(mTop)) : mTop;
-        mOutputRight = (Float.isNaN(mRight))
-                ? context.getFloat(Utils.idFromNan(mRight)) : mRight;
-        mOutputBottom = (Float.isNaN(mBottom))
-                ? context.getFloat(Utils.idFromNan(mBottom)) : mBottom;
+        mOutputLeft = Float.isNaN(mLeft) ? context.getFloat(Utils.idFromNan(mLeft)) : mLeft;
+        mOutputTop = Float.isNaN(mTop) ? context.getFloat(Utils.idFromNan(mTop)) : mTop;
+        mOutputRight = Float.isNaN(mRight) ? context.getFloat(Utils.idFromNan(mRight)) : mRight;
+        mOutputBottom = Float.isNaN(mBottom) ? context.getFloat(Utils.idFromNan(mBottom)) : mBottom;
     }
 
     @Override
@@ -93,8 +84,17 @@
 
     @Override
     public String toString() {
-        return "DrawBitmap (desc=" + mDescriptionId + ")" + mLeft + " " + mTop
-                + " " + mRight + " " + mBottom + ";";
+        return "DrawBitmap (desc="
+                + mDescriptionId
+                + ")"
+                + mLeft
+                + " "
+                + mTop
+                + " "
+                + mRight
+                + " "
+                + mBottom
+                + ";";
     }
 
     public static void read(WireBuffer buffer, List<Operation> operations) {
@@ -117,13 +117,14 @@
         return OP_CODE;
     }
 
-    public static void apply(WireBuffer buffer,
-                             int id,
-                             float left,
-                             float top,
-                             float right,
-                             float bottom,
-                             int descriptionId) {
+    public static void apply(
+            WireBuffer buffer,
+            int id,
+            float left,
+            float top,
+            float right,
+            float bottom,
+            int descriptionId) {
         buffer.start(Operations.DRAW_BITMAP);
         buffer.writeInt(id);
         buffer.writeFloat(left);
@@ -134,26 +135,18 @@
     }
 
     public static void documentation(DocumentationBuilder doc) {
-        doc.operation("Draw Operations",
-                        OP_CODE,
-                        CLASS_NAME)
+        doc.operation("Draw Operations", OP_CODE, CLASS_NAME)
                 .description("Draw a bitmap")
                 .field(INT, "id", "id of float")
-                .field(FLOAT, "left",
-                        "The left side of the image")
-                .field(FLOAT, "top",
-                        "The top of the image")
-                .field(FLOAT, "right",
-                        "The right side of the image")
-                .field(FLOAT, "bottom",
-                        "The bottom of the image")
+                .field(FLOAT, "left", "The left side of the image")
+                .field(FLOAT, "top", "The top of the image")
+                .field(FLOAT, "right", "The right side of the image")
+                .field(FLOAT, "bottom", "The bottom of the image")
                 .field(INT, "descriptionId", "id of string");
     }
 
+    @Override
     public void paint(PaintContext context) {
-        context.drawBitmap(mId, mOutputLeft,
-                mOutputTop,
-                mOutputRight,
-                mOutputBottom);
+        context.drawBitmap(mId, mOutputLeft, mOutputTop, mOutputRight, mOutputBottom);
     }
 }
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBitmapInt.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBitmapInt.java
index 561d527..fc74827 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBitmapInt.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBitmapInt.java
@@ -15,20 +15,17 @@
  */
 package com.android.internal.widget.remotecompose.core.operations;
 
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
-
 import com.android.internal.widget.remotecompose.core.Operation;
 import com.android.internal.widget.remotecompose.core.Operations;
 import com.android.internal.widget.remotecompose.core.PaintContext;
 import com.android.internal.widget.remotecompose.core.PaintOperation;
 import com.android.internal.widget.remotecompose.core.WireBuffer;
 import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
 
 import java.util.List;
 
-/**
- * Operation to draw a given cached bitmap
- */
+/** Operation to draw a given cached bitmap */
 public class DrawBitmapInt extends PaintOperation {
     private static final int OP_CODE = Operations.DRAW_BITMAP_INT;
     private static final String CLASS_NAME = "DrawBitmapInt";
@@ -43,16 +40,17 @@
     int mDstBottom;
     int mContentDescId = 0;
 
-    public DrawBitmapInt(int imageId,
-                         int srcLeft,
-                         int srcTop,
-                         int srcRight,
-                         int srcBottom,
-                         int dstLeft,
-                         int dstTop,
-                         int dstRight,
-                         int dstBottom,
-                         int cdId) {
+    public DrawBitmapInt(
+            int imageId,
+            int srcLeft,
+            int srcTop,
+            int srcRight,
+            int srcBottom,
+            int dstLeft,
+            int dstTop,
+            int dstRight,
+            int dstBottom,
+            int cdId) {
         this.mImageId = imageId;
         this.mSrcLeft = srcLeft;
         this.mSrcTop = srcTop;
@@ -67,18 +65,44 @@
 
     @Override
     public void write(WireBuffer buffer) {
-        apply(buffer, mImageId, mSrcLeft, mSrcTop, mSrcRight, mSrcBottom,
-                mDstLeft, mDstTop, mDstRight, mDstBottom, mContentDescId);
+        apply(
+                buffer,
+                mImageId,
+                mSrcLeft,
+                mSrcTop,
+                mSrcRight,
+                mSrcBottom,
+                mDstLeft,
+                mDstTop,
+                mDstRight,
+                mDstBottom,
+                mContentDescId);
     }
 
     @Override
     public String toString() {
-        return "DRAW_BITMAP_INT " + mImageId + " on " + mSrcLeft + " " + mSrcTop
-                + " " + mSrcRight + " " + mSrcBottom + " "
-                + "- " + mDstLeft + " " + mDstTop + " " + mDstRight + " " + mDstBottom + ";";
+        return "DRAW_BITMAP_INT "
+                + mImageId
+                + " on "
+                + mSrcLeft
+                + " "
+                + mSrcTop
+                + " "
+                + mSrcRight
+                + " "
+                + mSrcBottom
+                + " "
+                + "- "
+                + mDstLeft
+                + " "
+                + mDstTop
+                + " "
+                + mDstRight
+                + " "
+                + mDstBottom
+                + ";";
     }
 
-
     public static String name() {
         return CLASS_NAME;
     }
@@ -87,10 +111,18 @@
         return OP_CODE;
     }
 
-    public static void apply(WireBuffer buffer, int imageId,
-                             int srcLeft, int srcTop, int srcRight, int srcBottom,
-                             int dstLeft, int dstTop, int dstRight, int dstBottom,
-                             int cdId) {
+    public static void apply(
+            WireBuffer buffer,
+            int imageId,
+            int srcLeft,
+            int srcTop,
+            int srcRight,
+            int srcBottom,
+            int dstLeft,
+            int dstTop,
+            int dstRight,
+            int dstBottom,
+            int cdId) {
         buffer.start(Operations.DRAW_BITMAP_INT);
         buffer.writeInt(imageId);
         buffer.writeInt(srcLeft);
@@ -115,41 +147,41 @@
         int dstRight = buffer.readInt();
         int dstBottom = buffer.readInt();
         int cdId = buffer.readInt();
-        DrawBitmapInt op = new DrawBitmapInt(imageId, sLeft, srcTop, srcRight, srcBottom,
-                dstLeft, dstTop, dstRight, dstBottom, cdId);
+        DrawBitmapInt op =
+                new DrawBitmapInt(
+                        imageId, sLeft, srcTop, srcRight, srcBottom, dstLeft, dstTop, dstRight,
+                        dstBottom, cdId);
 
         operations.add(op);
     }
 
-
     public static void documentation(DocumentationBuilder doc) {
-        doc.operation("Draw Operations",
-                        OP_CODE,
-                        CLASS_NAME)
+        doc.operation("Draw Operations", OP_CODE, CLASS_NAME)
                 .description("Draw a bitmap using integer coordinates")
-                .field(INT, "id", "id of bitmap")
-                .field(INT, "srcLeft",
-                        "The left side of the image")
-                .field(INT, "srcTop",
-                        "The top of the image")
-                .field(INT, "srcRight",
-                        "The right side of the image")
-                .field(INT, "srcBottom",
-                        "The bottom of the image")
-                .field(INT, "dstLeft",
-                        "The left side of the image")
-                .field(INT, "dstTop",
-                        "The top of the image")
-                .field(INT, "dstRight",
-                        "The right side of the image")
-                .field(INT, "dstBottom",
-                        "The bottom of the image")
-                .field(INT, "cdId", "id of string");
+                .field(DocumentedOperation.INT, "id", "id of bitmap")
+                .field(DocumentedOperation.INT, "srcLeft", "The left side of the image")
+                .field(DocumentedOperation.INT, "srcTop", "The top of the image")
+                .field(DocumentedOperation.INT, "srcRight", "The right side of the image")
+                .field(DocumentedOperation.INT, "srcBottom", "The bottom of the image")
+                .field(DocumentedOperation.INT, "dstLeft", "The left side of the image")
+                .field(DocumentedOperation.INT, "dstTop", "The top of the image")
+                .field(DocumentedOperation.INT, "dstRight", "The right side of the image")
+                .field(DocumentedOperation.INT, "dstBottom", "The bottom of the image")
+                .field(DocumentedOperation.INT, "cdId", "id of string");
     }
 
     @Override
     public void paint(PaintContext context) {
-        context.drawBitmap(mImageId, mSrcLeft, mSrcTop, mSrcRight, mSrcBottom,
-                mDstLeft, mDstTop, mDstRight, mDstBottom, mContentDescId);
+        context.drawBitmap(
+                mImageId,
+                mSrcLeft,
+                mSrcTop,
+                mSrcRight,
+                mSrcBottom,
+                mDstLeft,
+                mDstTop,
+                mDstRight,
+                mDstBottom,
+                mContentDescId);
     }
 }
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBitmapScaled.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBitmapScaled.java
new file mode 100644
index 0000000..22742c6
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBitmapScaled.java
@@ -0,0 +1,319 @@
+/*
+ * 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.internal.widget.remotecompose.core.operations;
+
+import com.android.internal.widget.remotecompose.core.Operation;
+import com.android.internal.widget.remotecompose.core.Operations;
+import com.android.internal.widget.remotecompose.core.PaintContext;
+import com.android.internal.widget.remotecompose.core.PaintOperation;
+import com.android.internal.widget.remotecompose.core.RemoteContext;
+import com.android.internal.widget.remotecompose.core.VariableSupport;
+import com.android.internal.widget.remotecompose.core.WireBuffer;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
+import com.android.internal.widget.remotecompose.core.operations.utilities.ImageScaling;
+
+import java.util.List;
+
+/** Operation to draw a given cached bitmap */
+public class DrawBitmapScaled extends PaintOperation implements VariableSupport {
+    private static final int OP_CODE = Operations.DRAW_BITMAP_SCALED;
+    private static final String CLASS_NAME = "DrawBitmapScaled";
+    int mImageId;
+    float mSrcLeft, mOutSrcLeft;
+    float mSrcTop, mOutSrcTop;
+    float mSrcRight, mOutSrcRight;
+    float mSrcBottom, mOutSrcBottom;
+    float mDstLeft, mOutDstLeft;
+    float mDstTop, mOutDstTop;
+    float mDstRight, mOutDstRight;
+    float mDstBottom, mOutDstBottom;
+    int mContentDescId;
+    float mScaleFactor, mOutScaleFactor;
+    int mScaleType;
+
+    ImageScaling mScaling = new ImageScaling();
+    public static final int SCALE_NONE = ImageScaling.SCALE_NONE;
+    public static final int SCALE_INSIDE = ImageScaling.SCALE_INSIDE;
+    public static final int SCALE_FILL_WIDTH = ImageScaling.SCALE_FILL_WIDTH;
+    public static final int SCALE_FILL_HEIGHT = ImageScaling.SCALE_FILL_HEIGHT;
+    public static final int SCALE_FIT = ImageScaling.SCALE_FIT;
+    public static final int SCALE_CROP = ImageScaling.SCALE_CROP;
+    public static final int SCALE_FILL_BOUNDS = ImageScaling.SCALE_FILL_BOUNDS;
+    public static final int SCALE_FIXED_SCALE = ImageScaling.SCALE_FIXED_SCALE;
+
+    public DrawBitmapScaled(
+            int imageId,
+            float srcLeft,
+            float srcTop,
+            float srcRight,
+            float srcBottom,
+            float dstLeft,
+            float dstTop,
+            float dstRight,
+            float dstBottom,
+            int type,
+            float scale,
+            int cdId) {
+        this.mImageId = imageId;
+        mOutSrcLeft = mSrcLeft = srcLeft;
+        mOutSrcTop = mSrcTop = srcTop;
+        mOutSrcRight = mSrcRight = srcRight;
+        mOutSrcBottom = mSrcBottom = srcBottom;
+        mOutDstLeft = mDstLeft = dstLeft;
+        mOutDstTop = mDstTop = dstTop;
+        mOutDstRight = mDstRight = dstRight;
+        mOutDstBottom = mDstBottom = dstBottom;
+        mScaleType = type;
+        mOutScaleFactor = mScaleFactor = scale;
+        this.mContentDescId = cdId;
+    }
+
+    @Override
+    public void updateVariables(RemoteContext context) {
+        mOutSrcLeft =
+                Float.isNaN(mSrcLeft) ? context.getFloat(Utils.idFromNan(mSrcLeft)) : mSrcLeft;
+        mOutSrcTop = Float.isNaN(mSrcTop) ? context.getFloat(Utils.idFromNan(mSrcTop)) : mSrcTop;
+        mOutSrcRight =
+                Float.isNaN(mSrcRight) ? context.getFloat(Utils.idFromNan(mSrcRight)) : mSrcRight;
+        mOutSrcBottom =
+                Float.isNaN(mSrcBottom)
+                        ? context.getFloat(Utils.idFromNan(mSrcBottom))
+                        : mSrcBottom;
+        mOutDstLeft =
+                Float.isNaN(mDstLeft) ? context.getFloat(Utils.idFromNan(mDstLeft)) : mDstLeft;
+        mOutDstTop = Float.isNaN(mDstTop) ? context.getFloat(Utils.idFromNan(mDstTop)) : mDstTop;
+        mOutDstRight =
+                Float.isNaN(mDstRight) ? context.getFloat(Utils.idFromNan(mDstRight)) : mDstRight;
+        mOutDstBottom =
+                Float.isNaN(mDstBottom)
+                        ? context.getFloat(Utils.idFromNan(mDstBottom))
+                        : mDstBottom;
+        mOutScaleFactor =
+                Float.isNaN(mScaleFactor)
+                        ? context.getFloat(Utils.idFromNan(mScaleFactor))
+                        : mScaleFactor;
+    }
+
+    @Override
+    public void registerListening(RemoteContext context) {
+        register(context, mSrcLeft);
+        register(context, mSrcTop);
+        register(context, mSrcRight);
+        register(context, mSrcBottom);
+        register(context, mDstLeft);
+        register(context, mDstTop);
+        register(context, mDstRight);
+        register(context, mDstBottom);
+        register(context, mScaleFactor);
+    }
+
+    private void register(RemoteContext context, float value) {
+        if (Float.isNaN(value)) {
+            context.listensTo(Utils.idFromNan(value), this);
+        }
+    }
+
+    static String str(float v) {
+        String s = "  " + (int) v;
+        return s.substring(s.length() - 3);
+    }
+
+    void print(String str, float left, float top, float right, float bottom) {
+        String s = str;
+        s += str(left) + ", " + str(top) + ", " + str(right) + ", " + str(bottom) + ", ";
+        s += " [" + str(right - left) + " x " + str(bottom - top) + "]";
+        System.out.println(s);
+    }
+
+    @Override
+    public void write(WireBuffer buffer) {
+        apply(
+                buffer,
+                mImageId,
+                mSrcLeft,
+                mSrcTop,
+                mSrcRight,
+                mSrcBottom,
+                mDstLeft,
+                mDstTop,
+                mDstRight,
+                mDstBottom,
+                mScaleType,
+                mScaleFactor,
+                mContentDescId);
+    }
+
+    @Override
+    public String toString() {
+        return "DrawBitmapScaled "
+                + mImageId
+                + " ["
+                + Utils.floatToString(mSrcLeft, mOutSrcLeft)
+                + " "
+                + Utils.floatToString(mSrcTop, mOutSrcTop)
+                + " "
+                + Utils.floatToString(mSrcRight, mOutSrcRight)
+                + " "
+                + Utils.floatToString(mSrcBottom, mOutSrcBottom)
+                + "] "
+                + "- ["
+                + Utils.floatToString(mDstLeft, mOutDstLeft)
+                + " "
+                + Utils.floatToString(mDstTop, mOutDstTop)
+                + " "
+                + Utils.floatToString(mDstRight, mOutDstRight)
+                + " "
+                + Utils.floatToString(mDstBottom, mOutDstBottom)
+                + "] "
+                + " "
+                + mScaleType
+                + " "
+                + Utils.floatToString(mScaleFactor, mOutScaleFactor);
+    }
+
+    public static String name() {
+        return CLASS_NAME;
+    }
+
+    public static int id() {
+        return OP_CODE;
+    }
+
+    public static void apply(
+            WireBuffer buffer,
+            int imageId,
+            float srcLeft,
+            float srcTop,
+            float srcRight,
+            float srcBottom,
+            float dstLeft,
+            float dstTop,
+            float dstRight,
+            float dstBottom,
+            int scaleType,
+            float scaleFactor,
+            int cdId) {
+        buffer.start(OP_CODE);
+        buffer.writeInt(imageId);
+
+        buffer.writeFloat(srcLeft);
+        buffer.writeFloat(srcTop);
+        buffer.writeFloat(srcRight);
+        buffer.writeFloat(srcBottom);
+
+        buffer.writeFloat(dstLeft);
+        buffer.writeFloat(dstTop);
+        buffer.writeFloat(dstRight);
+        buffer.writeFloat(dstBottom);
+
+        buffer.writeInt(scaleType);
+        buffer.writeFloat(scaleFactor);
+        buffer.writeInt(cdId);
+    }
+
+    public static void read(WireBuffer buffer, List<Operation> operations) {
+        int imageId = buffer.readInt();
+
+        float sLeft = buffer.readFloat();
+        float srcTop = buffer.readFloat();
+        float srcRight = buffer.readFloat();
+        float srcBottom = buffer.readFloat();
+
+        float dstLeft = buffer.readFloat();
+        float dstTop = buffer.readFloat();
+        float dstRight = buffer.readFloat();
+        float dstBottom = buffer.readFloat();
+        int scaleType = buffer.readInt();
+        float scaleFactor = buffer.readFloat();
+        int cdId = buffer.readInt();
+        DrawBitmapScaled op =
+                new DrawBitmapScaled(
+                        imageId,
+                        sLeft,
+                        srcTop,
+                        srcRight,
+                        srcBottom,
+                        dstLeft,
+                        dstTop,
+                        dstRight,
+                        dstBottom,
+                        scaleType,
+                        scaleFactor,
+                        cdId);
+
+        operations.add(op);
+    }
+
+    public static void documentation(DocumentationBuilder doc) {
+        doc.operation("Draw Operations", OP_CODE, CLASS_NAME)
+                .description("Draw a bitmap using integer coordinates")
+                .field(DocumentedOperation.INT, "id", "id of bitmap")
+                .field(DocumentedOperation.FLOAT, "srcLeft", "The left side of the image")
+                .field(DocumentedOperation.FLOAT, "srcTop", "The top of the image")
+                .field(DocumentedOperation.FLOAT, "srcRight", "The right side of the image")
+                .field(DocumentedOperation.FLOAT, "srcBottom", "The bottom of the output")
+                .field(DocumentedOperation.FLOAT, "dstLeft", "The left side of the output")
+                .field(DocumentedOperation.FLOAT, "dstTop", "The top of the output")
+                .field(DocumentedOperation.FLOAT, "dstRight", "The right side of the output")
+                .field(DocumentedOperation.INT, "type", "type of auto scaling")
+                .field(DocumentedOperation.INT, "scaleFactor", "for allowed")
+                .field(DocumentedOperation.INT, "cdId", "id of string");
+    }
+
+    //    private String typeToString(int type) {
+    //        String[] typeString = {
+    //            "none",
+    //            "inside",
+    //            "fill_width",
+    //            "fill_height",
+    //            "fit",
+    //            "crop",
+    //            "fill_bounds",
+    //            "fixed_scale"
+    //        };
+    //        return typeString[type];
+    //    }
+
+    @Override
+    public void paint(PaintContext context) {
+        mScaling.setup(
+                mOutSrcLeft,
+                mOutSrcTop,
+                mOutSrcRight,
+                mOutSrcBottom,
+                mOutDstLeft,
+                mOutDstTop,
+                mOutDstRight,
+                mOutDstBottom,
+                mScaleType,
+                mOutScaleFactor);
+        context.save();
+        context.clipRect(mOutDstLeft, mOutDstTop, mOutDstRight, mOutDstBottom);
+        context.drawBitmap(
+                mImageId,
+                (int) mOutSrcLeft,
+                (int) mOutSrcTop,
+                (int) mOutSrcRight,
+                (int) mOutSrcBottom,
+                (int) mScaling.mFinalDstLeft,
+                (int) mScaling.mFinalDstTop,
+                (int) mScaling.mFinalDstRight,
+                (int) mScaling.mFinalDstBottom,
+                mContentDescId);
+        context.restore();
+    }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawCircle.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawCircle.java
index e39a191..04f095a 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawCircle.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawCircle.java
@@ -1,12 +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.internal.widget.remotecompose.core.operations;
 
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.FLOAT;
-
 import com.android.internal.widget.remotecompose.core.Operation;
 import com.android.internal.widget.remotecompose.core.Operations;
 import com.android.internal.widget.remotecompose.core.PaintContext;
 import com.android.internal.widget.remotecompose.core.WireBuffer;
 import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
 
 import java.util.List;
 
@@ -28,29 +42,25 @@
     }
 
     public static void documentation(DocumentationBuilder doc) {
-        doc.operation("Canvas Operations",
-                        OP_CODE,
-                        CLASS_NAME)
+        doc.operation("Canvas Operations", OP_CODE, CLASS_NAME)
                 .description("Draw a Circle")
-                .field(FLOAT, "centerX",
+                .field(
+                        DocumentedOperation.FLOAT,
+                        "centerX",
                         "The x-coordinate of the center of the circle to be drawn")
-                .field(FLOAT, "centerY",
+                .field(
+                        DocumentedOperation.FLOAT,
+                        "centerY",
                         "The y-coordinate of the center of the circle to be drawn")
-                .field(FLOAT, "radius",
-                        "The radius of the circle to be drawn");
+                .field(DocumentedOperation.FLOAT, "radius", "The radius of the circle to be drawn");
     }
 
-    protected void write(WireBuffer buffer,
-                         float v1,
-                         float v2,
-                         float v3) {
+    @Override
+    protected void write(WireBuffer buffer, float v1, float v2, float v3) {
         apply(buffer, v1, v2, v3);
     }
 
-    public DrawCircle(
-            float left,
-            float top,
-            float right) {
+    public DrawCircle(float left, float top, float right) {
         super(left, top, right);
         mName = CLASS_NAME;
     }
@@ -68,10 +78,7 @@
      * @param y1
      * @param x2
      */
-    public static void apply(WireBuffer buffer,
-                      float x1,
-                      float y1,
-                      float x2) {
+    public static void apply(WireBuffer buffer, float x1, float y1, float x2) {
         buffer.start(OP_CODE);
         buffer.writeFloat(x1);
         buffer.writeFloat(y1);
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawLine.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawLine.java
index a7276b5..dacbb03 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawLine.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawLine.java
@@ -15,14 +15,13 @@
  */
 package com.android.internal.widget.remotecompose.core.operations;
 
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.FLOAT;
-
 import com.android.internal.widget.remotecompose.core.Operation;
 import com.android.internal.widget.remotecompose.core.Operations;
 import com.android.internal.widget.remotecompose.core.PaintContext;
 import com.android.internal.widget.remotecompose.core.SerializableToString;
 import com.android.internal.widget.remotecompose.core.WireBuffer;
 import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
 import com.android.internal.widget.remotecompose.core.operations.utilities.StringSerializer;
 
 import java.util.List;
@@ -36,7 +35,6 @@
         read(m, buffer, operations);
     }
 
-
     public static int id() {
         return OP_CODE;
     }
@@ -46,33 +44,32 @@
     }
 
     public static void documentation(DocumentationBuilder doc) {
-        doc.operation("Canvas Operations",
-                        OP_CODE,
-                        CLASS_NAME)
+        doc.operation("Canvas Operations", OP_CODE, CLASS_NAME)
                 .description("Draw a line segment")
-                .field(FLOAT, "startX",
+                .field(
+                        DocumentedOperation.FLOAT,
+                        "startX",
                         "The x-coordinate of the start point of the line")
-                .field(FLOAT, "startY",
+                .field(
+                        DocumentedOperation.FLOAT,
+                        "startY",
                         "The y-coordinate of the start point of the line")
-                .field(FLOAT, "endX",
+                .field(
+                        DocumentedOperation.FLOAT,
+                        "endX",
                         "The x-coordinate of the end point of the line")
-                .field(FLOAT, "endY",
+                .field(
+                        DocumentedOperation.FLOAT,
+                        "endY",
                         "The y-coordinate of the end point of the line");
     }
 
-    protected void write(WireBuffer buffer,
-                         float v1,
-                         float v2,
-                         float v3,
-                         float v4) {
+    @Override
+    protected void write(WireBuffer buffer, float v1, float v2, float v3, float v4) {
         apply(buffer, v1, v2, v3, v4);
     }
 
-    public DrawLine(
-            float left,
-            float top,
-            float right,
-            float bottom) {
+    public DrawLine(float left, float top, float right, float bottom) {
         super(left, top, right, bottom);
         mName = "DrawLine";
     }
@@ -91,14 +88,11 @@
      * @param x2 end x of the line
      * @param y2 end y of the line
      */
-    public static void apply(WireBuffer buffer,
-                             float x1,
-                             float y1,
-                             float x2,
-                             float y2) {
+    public static void apply(WireBuffer buffer, float x1, float y1, float x2, float y2) {
         write(buffer, OP_CODE, x1, y1, x2, y2);
     }
 
+    @Override
     public void serializeToString(int indent, StringSerializer serializer) {
         String x1 = "" + mX1;
         if (Float.isNaN(mX1Value)) {
@@ -116,8 +110,6 @@
         if (Float.isNaN(mY2Value)) {
             y2 = "[" + Utils.idFromNan(mY2Value) + " = " + mY2 + "]";
         }
-        serializer.append(indent, CLASS_NAME
-                + "(" + x1 + ", " + y1 + ", " + x2 + ", " +  y2 + ")"
-        );
+        serializer.append(indent, CLASS_NAME + "(" + x1 + ", " + y1 + ", " + x2 + ", " + y2 + ")");
     }
 }
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawOval.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawOval.java
index 01761ef..5d498e8 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawOval.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawOval.java
@@ -15,13 +15,12 @@
  */
 package com.android.internal.widget.remotecompose.core.operations;
 
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.FLOAT;
-
 import com.android.internal.widget.remotecompose.core.Operation;
 import com.android.internal.widget.remotecompose.core.Operations;
 import com.android.internal.widget.remotecompose.core.PaintContext;
 import com.android.internal.widget.remotecompose.core.WireBuffer;
 import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
 
 import java.util.List;
 
@@ -43,25 +42,16 @@
     }
 
     public static void documentation(DocumentationBuilder doc) {
-        doc.operation("Canvas Operations",
-                        OP_CODE,
-                        CLASS_NAME)
+        doc.operation("Canvas Operations", OP_CODE, CLASS_NAME)
                 .description("Draw the specified oval")
-                .field(FLOAT, "left",
-                        "The left side of the oval")
-                .field(FLOAT, "top",
-                        "The top of the oval")
-                .field(FLOAT, "right",
-                        "The right side of the oval")
-                .field(FLOAT, "bottom",
-                        "The bottom of the oval");
+                .field(DocumentedOperation.FLOAT, "left", "The left side of the oval")
+                .field(DocumentedOperation.FLOAT, "top", "The top of the oval")
+                .field(DocumentedOperation.FLOAT, "right", "The right side of the oval")
+                .field(DocumentedOperation.FLOAT, "bottom", "The bottom of the oval");
     }
 
-    protected void write(WireBuffer buffer,
-                         float v1,
-                         float v2,
-                         float v3,
-                         float v4) {
+    @Override
+    protected void write(WireBuffer buffer, float v1, float v2, float v3, float v4) {
         apply(buffer, v1, v2, v3, v4);
     }
 
@@ -70,11 +60,7 @@
         apply(buffer, mX1, mY1, mX2, mY2);
     }
 
-    public DrawOval(
-            float left,
-            float top,
-            float right,
-            float bottom) {
+    public DrawOval(float left, float top, float right, float bottom) {
         super(left, top, right, bottom);
         mName = CLASS_NAME;
     }
@@ -83,6 +69,7 @@
     public void paint(PaintContext context) {
         context.drawOval(mX1, mY1, mX2, mY2);
     }
+
     /**
      * Writes out the DrawOval to the buffer
      *
@@ -92,11 +79,7 @@
      * @param x2 end x of the DrawOval
      * @param y2 end y of the DrawOval
      */
-    public static void apply(WireBuffer buffer,
-                             float x1,
-                             float y1,
-                             float x2,
-                             float y2) {
+    public static void apply(WireBuffer buffer, float x1, float y1, float x2, float y2) {
         write(buffer, OP_CODE, x1, y1, x2, y2);
     }
 }
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawPath.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawPath.java
index bc2c53a..ccbf3d9 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawPath.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawPath.java
@@ -15,14 +15,13 @@
  */
 package com.android.internal.widget.remotecompose.core.operations;
 
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
-
 import com.android.internal.widget.remotecompose.core.Operation;
 import com.android.internal.widget.remotecompose.core.Operations;
 import com.android.internal.widget.remotecompose.core.PaintContext;
 import com.android.internal.widget.remotecompose.core.PaintOperation;
 import com.android.internal.widget.remotecompose.core.WireBuffer;
 import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
 
 import java.util.List;
 
@@ -54,12 +53,10 @@
         operations.add(op);
     }
 
-
     public static String name() {
         return CLASS_NAME;
     }
 
-
     public static int id() {
         return Operations.DRAW_PATH;
     }
@@ -70,14 +67,11 @@
     }
 
     public static void documentation(DocumentationBuilder doc) {
-        doc.operation("Draw Operations",
-                        OP_CODE,
-                        CLASS_NAME)
+        doc.operation("Draw Operations", OP_CODE, CLASS_NAME)
                 .description("Draw a bitmap using integer coordinates")
-                .field(INT, "id", "id of path");
+                .field(DocumentedOperation.INT, "id", "id of path");
     }
 
-
     @Override
     public void paint(PaintContext context) {
         context.drawPath(mId, mStart, mEnd);
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawRect.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawRect.java
index ad17fe7..644011b 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawRect.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawRect.java
@@ -15,19 +15,16 @@
  */
 package com.android.internal.widget.remotecompose.core.operations;
 
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.FLOAT;
-
 import com.android.internal.widget.remotecompose.core.Operation;
 import com.android.internal.widget.remotecompose.core.Operations;
 import com.android.internal.widget.remotecompose.core.PaintContext;
 import com.android.internal.widget.remotecompose.core.WireBuffer;
 import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
 
 import java.util.List;
 
-/**
- * Draw a Rectangle
- */
+/** Draw a Rectangle */
 public class DrawRect extends DrawBase4 {
     private static final int OP_CODE = Operations.DRAW_RECT;
     private static final String CLASS_NAME = "DrawRect";
@@ -37,7 +34,6 @@
         read(m, buffer, operations);
     }
 
-
     public static int id() {
         return OP_CODE;
     }
@@ -47,34 +43,20 @@
     }
 
     public static void documentation(DocumentationBuilder doc) {
-        doc.operation("Canvas Operations",
-                        OP_CODE,
-                        CLASS_NAME)
+        doc.operation("Canvas Operations", OP_CODE, CLASS_NAME)
                 .description("Draw the specified rectangle")
-                .field(FLOAT, "left",
-                        "The left side of the rectangle")
-                .field(FLOAT, "top",
-                        "The top of the rectangle")
-                .field(FLOAT, "right",
-                        "The right side of the rectangle")
-                .field(FLOAT, "bottom",
-                        "The bottom of the rectangle");
+                .field(DocumentedOperation.FLOAT, "left", "The left side of the rectangle")
+                .field(DocumentedOperation.FLOAT, "top", "The top of the rectangle")
+                .field(DocumentedOperation.FLOAT, "right", "The right side of the rectangle")
+                .field(DocumentedOperation.FLOAT, "bottom", "The bottom of the rectangle");
     }
 
-
-    protected void write(WireBuffer buffer,
-                         float v1,
-                         float v2,
-                         float v3,
-                         float v4) {
+    @Override
+    protected void write(WireBuffer buffer, float v1, float v2, float v3, float v4) {
         apply(buffer, v1, v2, v3, v4);
     }
 
-    public DrawRect(
-            float left,
-            float top,
-            float right,
-            float bottom) {
+    public DrawRect(float left, float top, float right, float bottom) {
         super(left, top, right, bottom);
         mName = CLASS_NAME;
     }
@@ -88,16 +70,12 @@
      * Writes out the DrawRect to the buffer
      *
      * @param buffer buffer to write to
-     * @param x1     left x of rect
-     * @param y1     top y of the rect
-     * @param x2     right x of the rect
-     * @param y2     bottom y of the rect
+     * @param x1 left x of rect
+     * @param y1 top y of the rect
+     * @param x2 right x of the rect
+     * @param y2 bottom y of the rect
      */
-    public static void apply(WireBuffer buffer,
-                             float x1,
-                             float y1,
-                             float x2,
-                             float y2) {
+    public static void apply(WireBuffer buffer, float x1, float y1, float x2, float y2) {
         write(buffer, OP_CODE, x1, y1, x2, y2);
     }
 }
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawRoundRect.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawRoundRect.java
index 908e03a..64a3b28 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawRoundRect.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawRoundRect.java
@@ -15,24 +15,20 @@
  */
 package com.android.internal.widget.remotecompose.core.operations;
 
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.FLOAT;
-
 import com.android.internal.widget.remotecompose.core.Operation;
 import com.android.internal.widget.remotecompose.core.Operations;
 import com.android.internal.widget.remotecompose.core.PaintContext;
 import com.android.internal.widget.remotecompose.core.WireBuffer;
 import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
 
 import java.util.List;
 
-/**
- * Draw a rounded rectangle
- */
+/** Draw a rounded rectangle */
 public class DrawRoundRect extends DrawBase6 {
     private static final int OP_CODE = Operations.DRAW_ROUND_RECT;
     private static final String CLASS_NAME = "DrawRoundRect";
 
-
     public static void read(WireBuffer buffer, List<Operation> operations) {
         Maker m = DrawRoundRect::new;
         read(m, buffer, operations);
@@ -53,13 +49,8 @@
      * @param v5 The x-radius of the oval used to round the corners
      * @param v6 The y-radius of the oval used to round the corners
      */
-    public static void apply(WireBuffer buffer,
-                             float v1,
-                             float v2,
-                             float v3,
-                             float v4,
-                             float v5,
-                             float v6) {
+    public static void apply(
+            WireBuffer buffer, float v1, float v2, float v3, float v4, float v5, float v6) {
         buffer.start(OP_CODE);
         buffer.writeFloat(v1);
         buffer.writeFloat(v2);
@@ -69,50 +60,36 @@
         buffer.writeFloat(v6);
     }
 
-    protected void write(WireBuffer buffer,
-                         float v1,
-                         float v2,
-                         float v3,
-                         float v4,
-                         float v5,
-                         float v6) {
+    @Override
+    protected void write(
+            WireBuffer buffer, float v1, float v2, float v3, float v4, float v5, float v6) {
         apply(buffer, v1, v2, v3, v4, v5, v6);
     }
 
     public static void documentation(DocumentationBuilder doc) {
-        doc.operation("Canvas Operations",
-                        OP_CODE,
-                        CLASS_NAME)
+        doc.operation("Canvas Operations", OP_CODE, CLASS_NAME)
                 .description("Draw the specified round-rect")
-                .field(FLOAT, "left",
-                        "The left side of the rect")
-                .field(FLOAT, "top",
-                        "The top of the rect")
-                .field(FLOAT, "right",
-                        "The right side of the rect")
-                .field(FLOAT, "bottom",
-                        "The bottom of the rect")
-                .field(FLOAT, "rx",
+                .field(DocumentedOperation.FLOAT, "left", "The left side of the rect")
+                .field(DocumentedOperation.FLOAT, "top", "The top of the rect")
+                .field(DocumentedOperation.FLOAT, "right", "The right side of the rect")
+                .field(DocumentedOperation.FLOAT, "bottom", "The bottom of the rect")
+                .field(
+                        DocumentedOperation.FLOAT,
+                        "rx",
                         "The x-radius of the oval used to round the corners")
-                .field(FLOAT, "sweepAngle",
+                .field(
+                        DocumentedOperation.FLOAT,
+                        "sweepAngle",
                         "The y-radius of the oval used to round the corners");
     }
 
-
-    public DrawRoundRect(float v1,
-                         float v2,
-                         float v3,
-                         float v4,
-                         float v5,
-                         float v6) {
+    public DrawRoundRect(float v1, float v2, float v3, float v4, float v5, float v6) {
         super(v1, v2, v3, v4, v5, v6);
         mName = CLASS_NAME;
     }
 
     @Override
     public void paint(PaintContext context) {
-        context.drawRoundRect(mV1, mV2, mV3, mV4, mV5, mV6
-        );
+        context.drawRoundRect(mV1, mV2, mV3, mV4, mV5, mV6);
     }
-
 }
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawSector.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawSector.java
new file mode 100644
index 0000000..3cb1916
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawSector.java
@@ -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.internal.widget.remotecompose.core.operations;
+
+import com.android.internal.widget.remotecompose.core.Operation;
+import com.android.internal.widget.remotecompose.core.Operations;
+import com.android.internal.widget.remotecompose.core.PaintContext;
+import com.android.internal.widget.remotecompose.core.WireBuffer;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
+
+import java.util.List;
+
+public class DrawSector extends DrawBase6 {
+    public static final int OP_CODE = Operations.DRAW_SECTOR;
+    private static final String CLASS_NAME = "DrawSector";
+
+    public static void read(WireBuffer buffer, List<Operation> operations) {
+        Maker m = DrawSector::new;
+        read(m, buffer, operations);
+    }
+
+    public static int id() {
+        return OP_CODE;
+    }
+
+    /**
+     * Writes out the operation to the buffer
+     *
+     * @param buffer the buffer to write to
+     * @param v1 The left side of the Oval
+     * @param v2 The top of the Oval
+     * @param v3 The right side of the Oval
+     * @param v4 The bottom of the Oval
+     * @param v5 Starting angle (in degrees) where the arc begins
+     * @param v6 Sweep angle (in degrees) measured clockwise
+     */
+    public static void apply(
+            WireBuffer buffer, float v1, float v2, float v3, float v4, float v5, float v6) {
+        buffer.start(OP_CODE);
+        buffer.writeFloat(v1);
+        buffer.writeFloat(v2);
+        buffer.writeFloat(v3);
+        buffer.writeFloat(v4);
+        buffer.writeFloat(v5);
+        buffer.writeFloat(v6);
+    }
+
+    @Override
+    protected void write(
+            WireBuffer buffer, float v1, float v2, float v3, float v4, float v5, float v6) {
+        apply(buffer, v1, v2, v3, v4, v5, v6);
+    }
+
+    public static void documentation(DocumentationBuilder doc) {
+        doc.operation("Canvas Operations", OP_CODE, CLASS_NAME)
+                .description(
+                        "Draw the specified sector (pie shape)"
+                                + "which will be scaled to fit inside the specified oval")
+                .field(DocumentedOperation.FLOAT, "left", "The left side of the Oval")
+                .field(DocumentedOperation.FLOAT, "top", "The top of the Oval")
+                .field(DocumentedOperation.FLOAT, "right", "The right side of the Oval")
+                .field(DocumentedOperation.FLOAT, "bottom", "The bottom of the Oval")
+                .field(
+                        DocumentedOperation.FLOAT,
+                        "startAngle",
+                        "Starting angle (in degrees) where the arc begins")
+                .field(
+                        DocumentedOperation.FLOAT,
+                        "sweepAngle",
+                        "Sweep angle (in degrees) measured clockwise");
+    }
+
+    public DrawSector(float v1, float v2, float v3, float v4, float v5, float v6) {
+        super(v1, v2, v3, v4, v5, v6);
+        mName = "DrawSector";
+    }
+
+    @Override
+    public void paint(PaintContext context) {
+        context.drawSector(mV1, mV2, mV3, mV4, mV5, mV6);
+    }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawText.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawText.java
index 60dddc2..bcb7852 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawText.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawText.java
@@ -15,9 +15,6 @@
  */
 package com.android.internal.widget.remotecompose.core.operations;
 
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.BOOLEAN;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.FLOAT;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
 import static com.android.internal.widget.remotecompose.core.operations.Utils.floatToString;
 
 import com.android.internal.widget.remotecompose.core.Operation;
@@ -28,12 +25,11 @@
 import com.android.internal.widget.remotecompose.core.VariableSupport;
 import com.android.internal.widget.remotecompose.core.WireBuffer;
 import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
 
 import java.util.List;
 
-/**
- * Draw Text
- */
+/** Draw Text */
 public class DrawText extends PaintOperation implements VariableSupport {
     private static final int OP_CODE = Operations.DRAW_TEXT_RUN;
     private static final String CLASS_NAME = "DrawText";
@@ -48,14 +44,15 @@
     float mOutY = 0f;
     boolean mRtl = false;
 
-    public DrawText(int textID,
-                    int start,
-                    int end,
-                    int contextStart,
-                    int contextEnd,
-                    float x,
-                    float y,
-                    boolean rtl) {
+    public DrawText(
+            int textID,
+            int start,
+            int end,
+            int contextStart,
+            int contextEnd,
+            float x,
+            float y,
+            boolean rtl) {
         mTextID = textID;
         mStart = start;
         mEnd = end;
@@ -68,10 +65,8 @@
 
     @Override
     public void updateVariables(RemoteContext context) {
-        mOutX = (Float.isNaN(mX))
-                ? context.getFloat(Utils.idFromNan(mX)) : mX;
-        mOutY = (Float.isNaN(mY))
-                ? context.getFloat(Utils.idFromNan(mY)) : mY;
+        mOutX = Float.isNaN(mX) ? context.getFloat(Utils.idFromNan(mX)) : mX;
+        mOutY = Float.isNaN(mY) ? context.getFloat(Utils.idFromNan(mY)) : mY;
     }
 
     @Override
@@ -87,13 +82,20 @@
     @Override
     public void write(WireBuffer buffer) {
         apply(buffer, mTextID, mStart, mEnd, mContextStart, mContextEnd, mX, mY, mRtl);
-
     }
 
     @Override
     public String toString() {
-        return "DrawTextRun [" + mTextID + "] " + mStart + ", " + mEnd + ", "
-                + floatToString(mX, mOutX) + ", " + floatToString(mY, mOutY);
+        return "DrawTextRun ["
+                + mTextID
+                + "] "
+                + mStart
+                + ", "
+                + mEnd
+                + ", "
+                + floatToString(mX, mOutX)
+                + ", "
+                + floatToString(mY, mOutY);
     }
 
     public static void read(WireBuffer buffer, List<Operation> operations) {
@@ -114,7 +116,6 @@
         return CLASS_NAME;
     }
 
-
     public static int id() {
         return OP_CODE;
     }
@@ -122,25 +123,26 @@
     /**
      * Writes out the operation to the buffer
      *
-     * @param buffer       write the command to the buffer
-     * @param textID       id of the text
-     * @param start        Start position
-     * @param end          end position
+     * @param buffer write the command to the buffer
+     * @param textID id of the text
+     * @param start Start position
+     * @param end end position
      * @param contextStart start of the context
-     * @param contextEnd   end of the context
-     * @param x            position of where to draw
-     * @param y            position of where to draw
-     * @param rtl          is it Right to Left text
+     * @param contextEnd end of the context
+     * @param x position of where to draw
+     * @param y position of where to draw
+     * @param rtl is it Right to Left text
      */
-    public static void apply(WireBuffer buffer,
-                             int textID,
-                             int start,
-                             int end,
-                             int contextStart,
-                             int contextEnd,
-                             float x,
-                             float y,
-                             boolean rtl) {
+    public static void apply(
+            WireBuffer buffer,
+            int textID,
+            int start,
+            int end,
+            int contextStart,
+            int contextEnd,
+            float x,
+            float y,
+            boolean rtl) {
         buffer.start(Operations.DRAW_TEXT_RUN);
         buffer.writeInt(textID);
         buffer.writeInt(start);
@@ -152,33 +154,30 @@
         buffer.writeBoolean(rtl);
     }
 
-
     public static void documentation(DocumentationBuilder doc) {
-        doc.operation("Draw Operations",
-                        id(),
-                        CLASS_NAME)
+        doc.operation("Draw Operations", id(), CLASS_NAME)
                 .description("Draw a run of text, all in a single direction")
-                .field(INT, "textId", "id of bitmap")
-                .field(INT, "start",
+                .field(DocumentedOperation.INT, "textId", "id of bitmap")
+                .field(
+                        DocumentedOperation.INT,
+                        "start",
                         "The start of the text to render. -1=end of string")
-                .field(INT, "end",
-                        "The end of the text to render")
-                .field(INT, "contextStart",
+                .field(DocumentedOperation.INT, "end", "The end of the text to render")
+                .field(
+                        DocumentedOperation.INT,
+                        "contextStart",
                         "the index of the start of the shaping context")
-                .field(INT, "contextEnd",
+                .field(
+                        DocumentedOperation.INT,
+                        "contextEnd",
                         "the index of the end of the shaping context")
-                .field(FLOAT, "x",
-                        "The x position at which to draw the text")
-                .field(FLOAT, "y",
-                        "The y position at which to draw the text")
-                .field(BOOLEAN, "RTL",
-                        "Whether the run is in RTL direction");
+                .field(DocumentedOperation.FLOAT, "x", "The x position at which to draw the text")
+                .field(DocumentedOperation.FLOAT, "y", "The y position at which to draw the text")
+                .field(DocumentedOperation.BOOLEAN, "RTL", "Whether the run is in RTL direction");
     }
 
-
     @Override
     public void paint(PaintContext context) {
-        context.drawTextRun(mTextID, mStart, mEnd, mContextStart,
-                mContextEnd, mOutX, mOutY, mRtl);
+        context.drawTextRun(mTextID, mStart, mEnd, mContextStart, mContextEnd, mOutX, mOutY, mRtl);
     }
 }
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTextAnchored.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTextAnchored.java
index 242bc25..95a8766 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTextAnchored.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTextAnchored.java
@@ -15,9 +15,6 @@
  */
 package com.android.internal.widget.remotecompose.core.operations;
 
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.FLOAT;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
-
 import com.android.internal.widget.remotecompose.core.Operation;
 import com.android.internal.widget.remotecompose.core.Operations;
 import com.android.internal.widget.remotecompose.core.PaintContext;
@@ -26,12 +23,11 @@
 import com.android.internal.widget.remotecompose.core.VariableSupport;
 import com.android.internal.widget.remotecompose.core.WireBuffer;
 import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
 
 import java.util.List;
 
-/**
- * Draw Text in Anchored to a point
- */
+/** Draw Text in Anchored to a point */
 public class DrawTextAnchored extends PaintOperation implements VariableSupport {
     private static final int OP_CODE = Operations.DRAW_TEXT_ANCHOR;
     private static final String CLASS_NAME = "DrawTextAnchored";
@@ -49,12 +45,7 @@
     public static final int ANCHOR_TEXT_RTL = 1;
     public static final int ANCHOR_MONOSPACE_MEASURE = 2;
 
-    public DrawTextAnchored(int textID,
-                            float x,
-                            float y,
-                            float panX,
-                            float panY,
-                            int flags) {
+    public DrawTextAnchored(int textID, float x, float y, float panX, float panY, int flags) {
         mTextID = textID;
         mX = x;
         mY = y;
@@ -67,14 +58,10 @@
 
     @Override
     public void updateVariables(RemoteContext context) {
-        mOutX = (Float.isNaN(mX))
-                ? context.getFloat(Utils.idFromNan(mX)) : mX;
-        mOutY = (Float.isNaN(mY))
-                ? context.getFloat(Utils.idFromNan(mY)) : mY;
-        mOutPanX = (Float.isNaN(mPanX))
-                ? context.getFloat(Utils.idFromNan(mPanX)) : mPanX;
-        mOutPanY = (Float.isNaN(mPanY))
-                ? context.getFloat(Utils.idFromNan(mPanY)) : mPanY;
+        mOutX = Float.isNaN(mX) ? context.getFloat(Utils.idFromNan(mX)) : mX;
+        mOutY = Float.isNaN(mY) ? context.getFloat(Utils.idFromNan(mY)) : mY;
+        mOutPanX = Float.isNaN(mPanX) ? context.getFloat(Utils.idFromNan(mPanX)) : mPanX;
+        mOutPanY = Float.isNaN(mPanY) ? context.getFloat(Utils.idFromNan(mPanY)) : mPanY;
     }
 
     @Override
@@ -95,18 +82,22 @@
 
     @Override
     public void write(WireBuffer buffer) {
-        apply(buffer, mTextID, mX,
-                mY,
-                mPanX,
-                mPanY,
-                mFlags);
+        apply(buffer, mTextID, mX, mY, mPanX, mPanY, mFlags);
     }
 
     @Override
     public String toString() {
-        return "DrawTextAnchored [" + mTextID + "] " + floatToStr(mX) + ", "
-                + floatToStr(mY) + ", "
-                + floatToStr(mPanX) + ", " + floatToStr(mPanY) + ", "
+        return "DrawTextAnchored ["
+                + mTextID
+                + "] "
+                + floatToStr(mX)
+                + ", "
+                + floatToStr(mY)
+                + ", "
+                + floatToStr(mPanX)
+                + ", "
+                + floatToStr(mPanY)
+                + ", "
                 + Integer.toBinaryString(mFlags);
     }
 
@@ -125,10 +116,7 @@
         float panY = buffer.readFloat();
         int flags = buffer.readInt();
 
-        DrawTextAnchored op = new DrawTextAnchored(textID,
-                x, y,
-                panX, panY,
-                flags);
+        DrawTextAnchored op = new DrawTextAnchored(textID, x, y, panX, panY, flags);
 
         operations.add(op);
     }
@@ -152,13 +140,8 @@
      * @param panY The pan from top(-1) to bottom(1) 0 being centered
      * @param flags Change the behaviour
      */
-    public static void apply(WireBuffer buffer,
-                             int textID,
-                             float x,
-                             float y,
-                             float panX,
-                             float panY,
-                             int flags) {
+    public static void apply(
+            WireBuffer buffer, int textID, float x, float y, float panX, float panY, int flags) {
         buffer.start(OP_CODE);
         buffer.writeInt(textID);
         buffer.writeFloat(x);
@@ -169,25 +152,22 @@
     }
 
     public static void documentation(DocumentationBuilder doc) {
-        doc.operation("Draw Operations",
-                        OP_CODE,
-                        CLASS_NAME)
+        doc.operation("Draw Operations", OP_CODE, CLASS_NAME)
                 .description("Draw text centered about an anchor point")
-                .field(INT, "textId", "id of bitmap")
-                .field(FLOAT, "x",
-                        "The x-position of the anchor point")
-                .field(FLOAT, "y",
-                        "The y-position of the anchor point")
-                .field(FLOAT, "panX",
+                .field(DocumentedOperation.INT, "textId", "id of bitmap")
+                .field(DocumentedOperation.FLOAT, "x", "The x-position of the anchor point")
+                .field(DocumentedOperation.FLOAT, "y", "The y-position of the anchor point")
+                .field(
+                        DocumentedOperation.FLOAT,
+                        "panX",
                         "The pan from left(-1) to right(1) 0 being centered")
-                .field(FLOAT, "panY",
+                .field(
+                        DocumentedOperation.FLOAT,
+                        "panY",
                         "The pan from top(-1) to bottom(1) 0 being centered")
-                .field(INT, "flags",
-                        "Change the behaviour");
-
+                .field(DocumentedOperation.INT, "flags", "Change the behaviour");
     }
 
-
     float[] mBounds = new float[4];
 
     private float getHorizontalOffset() {
@@ -196,8 +176,7 @@
 
         float textWidth = scale * (mBounds[2] - mBounds[0]);
         float boxWidth = 0;
-        return (boxWidth - textWidth) * (1 + mOutPanX) / 2.f
-                - (scale * mBounds[0]);
+        return (boxWidth - textWidth) * (1 + mOutPanX) / 2.f - (scale * mBounds[0]);
     }
 
     private float getVerticalOffset() {
@@ -205,18 +184,18 @@
         float scale = 1.0f;
         float boxHeight = 0;
         float textHeight = scale * (mBounds[3] - mBounds[1]);
-        return (boxHeight - textHeight) * (1 - mOutPanY) / 2
-                - (scale * mBounds[1]);
+        return (boxHeight - textHeight) * (1 - mOutPanY) / 2 - (scale * mBounds[1]);
     }
 
     @Override
     public void paint(PaintContext context) {
-        int flags = ((mFlags & ANCHOR_MONOSPACE_MEASURE) != 0)
-                ? PaintContext.TEXT_MEASURE_MONOSPACE_WIDTH : 0;
+        int flags =
+                ((mFlags & ANCHOR_MONOSPACE_MEASURE) != 0)
+                        ? PaintContext.TEXT_MEASURE_MONOSPACE_WIDTH
+                        : 0;
         context.getTextBounds(mTextID, 0, -1, flags, mBounds);
         float x = mOutX + getHorizontalOffset();
-        float y = (Float.isNaN(mOutPanY)) ? mOutY : mOutY + getVerticalOffset();
-        context.drawTextRun(mTextID, 0, -1, 0, 1, x, y,
-                (mFlags & ANCHOR_TEXT_RTL) == 1);
+        float y = Float.isNaN(mOutPanY) ? mOutY : mOutY + getVerticalOffset();
+        context.drawTextRun(mTextID, 0, -1, 0, 1, x, y, (mFlags & ANCHOR_TEXT_RTL) == 1);
     }
 }
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTextOnPath.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTextOnPath.java
index d69362b..aefd6f3 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTextOnPath.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTextOnPath.java
@@ -15,10 +15,6 @@
  */
 package com.android.internal.widget.remotecompose.core.operations;
 
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.FLOAT;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
-import static com.android.internal.widget.remotecompose.core.operations.Utils.floatToString;
-
 import com.android.internal.widget.remotecompose.core.Operation;
 import com.android.internal.widget.remotecompose.core.Operations;
 import com.android.internal.widget.remotecompose.core.PaintContext;
@@ -27,13 +23,12 @@
 import com.android.internal.widget.remotecompose.core.VariableSupport;
 import com.android.internal.widget.remotecompose.core.WireBuffer;
 import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
 
 import java.util.List;
 
-/**
- * Draw text along a path.
- */
-public class DrawTextOnPath extends PaintOperation implements VariableSupport  {
+/** Draw text along a path. */
+public class DrawTextOnPath extends PaintOperation implements VariableSupport {
     private static final int OP_CODE = Operations.DRAW_TEXT_ON_PATH;
     private static final String CLASS_NAME = "DrawTextOnPath";
     int mPathId;
@@ -46,18 +41,16 @@
     public DrawTextOnPath(int textId, int pathId, float hOffset, float vOffset) {
         mPathId = pathId;
         mTextId = textId;
-        mOutHOffset = mHOffset = vOffset;
-        mOutVOffset = mVOffset = hOffset;
+        mOutHOffset = mHOffset = hOffset;
+        mOutVOffset = mVOffset = vOffset;
     }
 
-
     @Override
     public void updateVariables(RemoteContext context) {
-        mOutHOffset = (Float.isNaN(mHOffset))
-                ? context.getFloat(Utils.idFromNan(mHOffset)) : mHOffset;
-        mOutVOffset = (Float.isNaN(mVOffset))
-                ? context.getFloat(Utils.idFromNan(mVOffset)) : mVOffset;
-
+        mOutHOffset =
+                Float.isNaN(mHOffset) ? context.getFloat(Utils.idFromNan(mHOffset)) : mHOffset;
+        mOutVOffset =
+                Float.isNaN(mVOffset) ? context.getFloat(Utils.idFromNan(mVOffset)) : mVOffset;
     }
 
     @Override
@@ -77,16 +70,21 @@
 
     @Override
     public String toString() {
-        return "DrawTextOnPath [" + mTextId + "] [" + mPathId + "] "
-                + floatToString(mHOffset, mOutHOffset) + ", "
-                + floatToString(mVOffset, mOutVOffset);
+        return "DrawTextOnPath ["
+                + mTextId
+                + "] ["
+                + mPathId
+                + "] "
+                + Utils.floatToString(mHOffset, mOutHOffset)
+                + ", "
+                + Utils.floatToString(mVOffset, mOutVOffset);
     }
 
     public static void read(WireBuffer buffer, List<Operation> operations) {
         int textId = buffer.readInt();
         int pathId = buffer.readInt();
-        float hOffset = buffer.readFloat();
         float vOffset = buffer.readFloat();
+        float hOffset = buffer.readFloat();
         DrawTextOnPath op = new DrawTextOnPath(textId, pathId, hOffset, vOffset);
         operations.add(op);
     }
@@ -95,33 +93,26 @@
         return "DrawTextOnPath";
     }
 
-
     public static int id() {
         return Operations.DRAW_TEXT_ON_PATH;
     }
 
-    public static void apply(WireBuffer buffer, int textId, int pathId,
-                             float hOffset, float vOffset) {
+    public static void apply(
+            WireBuffer buffer, int textId, int pathId, float hOffset, float vOffset) {
         buffer.start(OP_CODE);
         buffer.writeInt(textId);
         buffer.writeInt(pathId);
-        buffer.writeFloat(hOffset);
         buffer.writeFloat(vOffset);
+        buffer.writeFloat(hOffset);
     }
 
     public static void documentation(DocumentationBuilder doc) {
-        doc.operation("Draw Operations",
-                        OP_CODE,
-                        CLASS_NAME)
+        doc.operation("Draw Operations", OP_CODE, CLASS_NAME)
                 .description("Draw text along path object")
-                .field(INT, "textId",
-                        "id of the text")
-                .field(INT, "pathId",
-                        "id of the path")
-                .field(FLOAT, "xOffset",
-                        "x Shift of the text")
-                .field(FLOAT, "yOffset",
-                        "y Shift of the text");
+                .field(DocumentedOperation.INT, "textId", "id of the text")
+                .field(DocumentedOperation.INT, "pathId", "id of the path")
+                .field(DocumentedOperation.FLOAT, "xOffset", "x Shift of the text")
+                .field(DocumentedOperation.FLOAT, "yOffset", "y Shift of the text");
     }
 
     @Override
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTweenPath.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTweenPath.java
index 3547263..b6d45d9 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTweenPath.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTweenPath.java
@@ -15,8 +15,6 @@
  */
 package com.android.internal.widget.remotecompose.core.operations;
 
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.FLOAT;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
 import static com.android.internal.widget.remotecompose.core.operations.Utils.floatToString;
 
 import com.android.internal.widget.remotecompose.core.Operation;
@@ -27,6 +25,7 @@
 import com.android.internal.widget.remotecompose.core.VariableSupport;
 import com.android.internal.widget.remotecompose.core.WireBuffer;
 import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
 
 import java.util.List;
 
@@ -42,12 +41,7 @@
     int mPath1Id;
     int mPath2Id;
 
-    public DrawTweenPath(
-            int path1Id,
-            int path2Id,
-            float tween,
-            float start,
-            float stop) {
+    public DrawTweenPath(int path1Id, int path2Id, float tween, float start, float stop) {
         mOutTween = mTween = tween;
         mOutStart = mStart = start;
         mOutStop = mStop = stop;
@@ -57,12 +51,9 @@
 
     @Override
     public void updateVariables(RemoteContext context) {
-        mOutTween = (Float.isNaN(mTween))
-                ? context.getFloat(Utils.idFromNan(mTween)) : mTween;
-        mOutStart = (Float.isNaN(mStart))
-                ? context.getFloat(Utils.idFromNan(mStart)) : mStart;
-        mOutStop = (Float.isNaN(mStop))
-                ? context.getFloat(Utils.idFromNan(mStop)) : mStop;
+        mOutTween = Float.isNaN(mTween) ? context.getFloat(Utils.idFromNan(mTween)) : mTween;
+        mOutStart = Float.isNaN(mStart) ? context.getFloat(Utils.idFromNan(mStart)) : mStart;
+        mOutStop = Float.isNaN(mStop) ? context.getFloat(Utils.idFromNan(mStop)) : mStop;
     }
 
     @Override
@@ -80,49 +71,44 @@
 
     @Override
     public void write(WireBuffer buffer) {
-        apply(buffer, mPath1Id,
-                mPath2Id,
-                mTween,
-                mStart,
-                mStop);
+        apply(buffer, mPath1Id, mPath2Id, mTween, mStart, mStop);
     }
 
     @Override
     public String toString() {
-        return "DrawTweenPath " + mPath1Id + " " + mPath2Id
-                + " " + floatToString(mTween, mOutTween)  + " "
-                + floatToString(mStart, mOutStart) + " "
-                + "- " + floatToString(mStop, mOutStop);
+        return "DrawTweenPath "
+                + mPath1Id
+                + " "
+                + mPath2Id
+                + " "
+                + floatToString(mTween, mOutTween)
+                + " "
+                + floatToString(mStart, mOutStart)
+                + " "
+                + "- "
+                + floatToString(mStop, mOutStop);
     }
 
-
     public static void read(WireBuffer buffer, List<Operation> operations) {
         int path1Id = buffer.readInt();
         int path2Id = buffer.readInt();
         float tween = buffer.readFloat();
         float start = buffer.readFloat();
         float stop = buffer.readFloat();
-        DrawTweenPath op = new DrawTweenPath(path1Id, path2Id,
-                tween, start, stop);
+        DrawTweenPath op = new DrawTweenPath(path1Id, path2Id, tween, start, stop);
         operations.add(op);
     }
 
-
     public static String name() {
         return "DrawTweenPath";
     }
 
-
     public static int id() {
         return Operations.DRAW_TWEEN_PATH;
     }
 
-    public static void apply(WireBuffer buffer,
-                      int path1Id,
-                      int path2Id,
-                      float tween,
-                      float start,
-                      float stop) {
+    public static void apply(
+            WireBuffer buffer, int path1Id, int path2Id, float tween, float start, float stop) {
         buffer.start(OP_CODE);
         buffer.writeInt(path1Id);
         buffer.writeInt(path2Id);
@@ -131,33 +117,18 @@
         buffer.writeFloat(stop);
     }
 
-
     public static void documentation(DocumentationBuilder doc) {
-        doc.operation("Draw Operations",
-                        OP_CODE,
-                        CLASS_NAME)
+        doc.operation("Draw Operations", OP_CODE, CLASS_NAME)
                 .description("Draw text along path object")
-                .field(INT, "pathId1",
-                        "id of path 1")
-                .field(INT, "pathId2",
-                        "id of path 2")
-                .field(FLOAT, "tween",
-                        "interpolate between the two paths")
-                .field(FLOAT, "start",
-                        "trim the start of the path")
-                .field(FLOAT, "yOffset",
-                        "trim the end of the path");
-
+                .field(DocumentedOperation.INT, "pathId1", "id of path 1")
+                .field(DocumentedOperation.INT, "pathId2", "id of path 2")
+                .field(DocumentedOperation.FLOAT, "tween", "interpolate between the two paths")
+                .field(DocumentedOperation.FLOAT, "start", "trim the start of the path")
+                .field(DocumentedOperation.FLOAT, "yOffset", "trim the end of the path");
     }
 
-
     @Override
     public void paint(PaintContext context) {
-        context.drawTweenPath(mPath1Id,
-                mPath2Id,
-                mOutTween,
-                mOutStart,
-                mOutStop);
+        context.drawTweenPath(mPath1Id, mPath2Id, mOutTween, mOutStart, mOutStop);
     }
-
 }
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/FloatConstant.java b/core/java/com/android/internal/widget/remotecompose/core/operations/FloatConstant.java
index 31b8ff6..765e150 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/FloatConstant.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/FloatConstant.java
@@ -15,21 +15,19 @@
  */
 package com.android.internal.widget.remotecompose.core.operations;
 
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.FLOAT;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.FLOAT;
 
 import com.android.internal.widget.remotecompose.core.Operation;
 import com.android.internal.widget.remotecompose.core.Operations;
 import com.android.internal.widget.remotecompose.core.RemoteContext;
 import com.android.internal.widget.remotecompose.core.WireBuffer;
 import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
 
 import java.util.List;
 
-/**
- * Operation to deal with Text data
- */
-public class FloatConstant implements Operation {
+/** Operation to deal with Text data */
+public class FloatConstant implements com.android.internal.widget.remotecompose.core.Operation {
     private static final int OP_CODE = Operations.DATA_FLOAT;
     private static final String CLASS_NAME = "FloatConstant";
     public int mTextId;
@@ -54,7 +52,6 @@
         return CLASS_NAME;
     }
 
-
     public static int id() {
         return OP_CODE;
     }
@@ -63,8 +60,8 @@
      * Writes out the operation to the buffer
      *
      * @param buffer write command to this buffer
-     * @param id     the id
-     * @param value  the value of the float
+     * @param id the id
+     * @param value the value of the float
      */
     public static void apply(WireBuffer buffer, int id, float value) {
         buffer.start(OP_CODE);
@@ -80,14 +77,10 @@
     }
 
     public static void documentation(DocumentationBuilder doc) {
-        doc.operation("Expressions Operations",
-                        OP_CODE,
-                        CLASS_NAME)
+        doc.operation("Expressions Operations", OP_CODE, CLASS_NAME)
                 .description("A float and its associated id")
-                .field(INT, "id", "id of float")
-                .field(FLOAT, "value",
-                        "32-bit float value");
-
+                .field(DocumentedOperation.INT, "id", "id of float")
+                .field(FLOAT, "value", "32-bit float value");
     }
 
     @Override
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/FloatExpression.java b/core/java/com/android/internal/widget/remotecompose/core/operations/FloatExpression.java
index e3df1eb..d717933 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/FloatExpression.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/FloatExpression.java
@@ -15,10 +15,10 @@
  */
 package com.android.internal.widget.remotecompose.core.operations;
 
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.FLOAT;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.FLOAT_ARRAY;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.SHORT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.FLOAT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.FLOAT_ARRAY;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.SHORT;
 
 import com.android.internal.widget.remotecompose.core.Operation;
 import com.android.internal.widget.remotecompose.core.Operations;
@@ -26,19 +26,17 @@
 import com.android.internal.widget.remotecompose.core.VariableSupport;
 import com.android.internal.widget.remotecompose.core.WireBuffer;
 import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
 import com.android.internal.widget.remotecompose.core.operations.utilities.AnimatedFloatExpression;
 import com.android.internal.widget.remotecompose.core.operations.utilities.NanMap;
 import com.android.internal.widget.remotecompose.core.operations.utilities.easing.FloatAnimation;
 
-import java.util.Arrays;
 import java.util.List;
 
 /**
- * Operation to deal with AnimatedFloats
- * This is designed to be an optimized calculation for things like
- * injecting the width of the component int draw rect
- * As well as supporting generalized animation floats.
- * The floats represent a RPN style calculator
+ * Operation to deal with AnimatedFloats This is designed to be an optimized calculation for things
+ * like injecting the width of the component int draw rect As well as supporting generalized
+ * animation floats. The floats represent a RPN style calculator
  */
 public class FloatExpression implements Operation, VariableSupport {
     private static final int OP_CODE = Operations.ANIMATED_FLOAT;
@@ -49,6 +47,7 @@
     public FloatAnimation mFloatAnimation;
     public float[] mPreCalcValue;
     private float mLastChange = Float.NaN;
+    private float mLastCalculatedValue = Float.NaN;
     AnimatedFloatExpression mExp = new AnimatedFloatExpression();
     public static final int MAX_EXPRESSION_SIZE = 32;
 
@@ -70,12 +69,12 @@
         boolean value_changed = false;
         for (int i = 0; i < mSrcValue.length; i++) {
             float v = mSrcValue[i];
-            if (Float.isNaN(v) && !AnimatedFloatExpression.isMathOperator(v)
+            if (Float.isNaN(v)
+                    && !AnimatedFloatExpression.isMathOperator(v)
                     && !NanMap.isDataVariable(v)) {
                 float newValue = context.getFloat(Utils.idFromNan(v));
                 if (mFloatAnimation != null) {
                     if (mPreCalcValue[i] != newValue) {
-                        mLastChange = context.getAnimationTime();
                         value_changed = true;
                         mPreCalcValue[i] = newValue;
                     }
@@ -86,8 +85,18 @@
                 mPreCalcValue[i] = mSrcValue[i];
             }
         }
+        float v = mLastCalculatedValue;
+        if (value_changed) { // inputs changed check if output changed
+            v = mExp.eval(mPreCalcValue, mPreCalcValue.length);
+            if (v != mLastCalculatedValue) {
+                mLastChange = context.getAnimationTime();
+                mLastCalculatedValue = v;
+            } else {
+                value_changed = false;
+            }
+        }
+
         if (value_changed && mFloatAnimation != null) {
-            float v = mExp.eval(Arrays.copyOf(mPreCalcValue, mPreCalcValue.length));
             if (Float.isNaN(mFloatAnimation.getTargetValue())) {
                 mFloatAnimation.setInitialValue(v);
             } else {
@@ -100,7 +109,8 @@
     @Override
     public void registerListening(RemoteContext context) {
         for (float v : mSrcValue) {
-            if (Float.isNaN(v) && !AnimatedFloatExpression.isMathOperator(v)
+            if (Float.isNaN(v)
+                    && !AnimatedFloatExpression.isMathOperator(v)
                     && !NanMap.isDataVariable(v)) {
                 context.listensTo(Utils.idFromNan(v), this);
             }
@@ -118,8 +128,9 @@
             float f = mFloatAnimation.get(t - mLastChange);
             context.loadFloat(mId, f);
         } else {
-            context.loadFloat(mId, mExp.eval(context.getCollectionsAccess(),
-                    Arrays.copyOf(mPreCalcValue, mPreCalcValue.length)));
+            context.loadFloat(
+                    mId,
+                    mExp.eval(context.getCollectionsAccess(), mPreCalcValue, mPreCalcValue.length));
         }
     }
 
@@ -137,11 +148,17 @@
             }
         }
         if (mPreCalcValue == null) {
-            return "FloatExpression[" + mId + "] = ("
-                    + AnimatedFloatExpression.toString(mSrcValue, labels) + ")";
+            return "FloatExpression["
+                    + mId
+                    + "] = ("
+                    + AnimatedFloatExpression.toString(mSrcValue, labels)
+                    + ")";
         }
-        return "FloatExpression[" + mId + "] = ("
-                + AnimatedFloatExpression.toString(mPreCalcValue, labels) + ")";
+        return "FloatExpression["
+                + mId
+                + "] = ("
+                + AnimatedFloatExpression.toString(mPreCalcValue, labels)
+                + ")";
     }
 
     public static String name() {
@@ -155,9 +172,9 @@
     /**
      * Writes out the operation to the buffer
      *
-     * @param buffer    The buffer to write to
-     * @param id        the id of the resulting float
-     * @param value     the float expression array
+     * @param buffer The buffer to write to
+     * @param id the id of the resulting float
+     * @param value the float expression array
      * @param animation the animation expression array
      */
     public static void apply(WireBuffer buffer, int id, float[] value, float[] animation) {
@@ -178,7 +195,6 @@
                 buffer.writeFloat(v);
             }
         }
-
     }
 
     public static void read(WireBuffer buffer, List<Operation> operations) {
@@ -207,16 +223,20 @@
     }
 
     public static void documentation(DocumentationBuilder doc) {
-        doc.operation("Expressions Operations",
-                        OP_CODE,
-                        CLASS_NAME)
+        doc.operation("Expressions Operations", OP_CODE, CLASS_NAME)
                 .description("A Float expression")
-                .field(INT, "id", "The id of the Color")
+                .field(DocumentedOperation.INT, "id", "The id of the Color")
                 .field(SHORT, "expression_length", "expression length")
                 .field(SHORT, "animation_length", "animation description length")
-                .field(FLOAT_ARRAY, "expression", "expression_length",
+                .field(
+                        FLOAT_ARRAY,
+                        "expression",
+                        "expression_length",
                         "Sequence of Floats representing and expression")
-                .field(FLOAT_ARRAY, "AnimationSpec", "animation_length",
+                .field(
+                        FLOAT_ARRAY,
+                        "AnimationSpec",
+                        "animation_length",
                         "Sequence of Floats representing animation curve")
                 .field(FLOAT, "duration", "> time in sec")
                 .field(INT, "bits", "> WRAP|INITALVALUE | TYPE ")
@@ -229,5 +249,4 @@
     public String deepToString(String indent) {
         return indent + toString();
     }
-
 }
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/Header.java b/core/java/com/android/internal/widget/remotecompose/core/operations/Header.java
index 099bce88..4f8516f 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/Header.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/Header.java
@@ -15,8 +15,8 @@
  */
 package com.android.internal.widget.remotecompose.core.operations;
 
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.LONG;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.LONG;
 
 import com.android.internal.widget.remotecompose.core.Operation;
 import com.android.internal.widget.remotecompose.core.Operations;
@@ -29,9 +29,9 @@
 
 /**
  * Describe some basic information for a RemoteCompose document
- * <p>
- * It encodes the version of the document (following semantic versioning) as well
- * as the dimensions of the document in pixels.
+ *
+ * <p>It encodes the version of the document (following semantic versioning) as well as the
+ * dimensions of the document in pixels.
  */
 public class Header implements RemoteComposeOperation {
     private static final int OP_CODE = Operations.HEADER;
@@ -50,21 +50,26 @@
     float mDensity;
     long mCapabilities;
 
-
     /**
-     * It encodes the version of the document (following semantic versioning) as well
-     * as the dimensions of the document in pixels.
+     * It encodes the version of the document (following semantic versioning) as well as the
+     * dimensions of the document in pixels.
      *
      * @param majorVersion the major version of the RemoteCompose document API
      * @param minorVersion the minor version of the RemoteCompose document API
      * @param patchVersion the patch version of the RemoteCompose document API
-     * @param width        the width of the RemoteCompose document
-     * @param height       the height of the RemoteCompose document
-     * @param density      the density at which the document was originally created
+     * @param width the width of the RemoteCompose document
+     * @param height the height of the RemoteCompose document
+     * @param density the density at which the document was originally created
      * @param capabilities bitmask field storing needed capabilities (unused for now)
      */
-    public Header(int majorVersion, int minorVersion, int patchVersion,
-                  int width, int height, float density, long capabilities) {
+    public Header(
+            int majorVersion,
+            int minorVersion,
+            int patchVersion,
+            int width,
+            int height,
+            float density,
+            long capabilities) {
         this.mMajorVersion = majorVersion;
         this.mMinorVersion = minorVersion;
         this.mPatchVersion = patchVersion;
@@ -81,9 +86,19 @@
 
     @Override
     public String toString() {
-        return "HEADER v" + mMajorVersion + "."
-                + mMinorVersion + "." + mPatchVersion + ", "
-                + mWidth + " x " + mHeight + " [" + mCapabilities + "]";
+        return "HEADER v"
+                + mMajorVersion
+                + "."
+                + mMinorVersion
+                + "."
+                + mPatchVersion
+                + ", "
+                + mWidth
+                + " x "
+                + mHeight
+                + " ["
+                + mCapabilities
+                + "]";
     }
 
     @Override
@@ -104,8 +119,8 @@
         return OP_CODE;
     }
 
-    public static void apply(WireBuffer buffer, int width, int height,
-                             float density, long capabilities) {
+    public static void apply(
+            WireBuffer buffer, int width, int height, float density, long capabilities) {
         buffer.start(OP_CODE);
         buffer.writeInt(MAJOR_VERSION); // major version number of the protocol
         buffer.writeInt(MINOR_VERSION); // minor version number of the protocol
@@ -125,15 +140,23 @@
         // float density = buffer.readFloat();
         float density = 1f;
         long capabilities = buffer.readLong();
-        Header header = new Header(majorVersion, minorVersion, patchVersion,
-                width, height, density, capabilities);
+        Header header =
+                new Header(
+                        majorVersion,
+                        minorVersion,
+                        patchVersion,
+                        width,
+                        height,
+                        density,
+                        capabilities);
         operations.add(header);
     }
 
     public static void documentation(DocumentationBuilder doc) {
         doc.operation("Protocol Operations", OP_CODE, CLASS_NAME)
-                .description("Document metadata, containing the version,"
-                        + " original size & density, capabilities mask")
+                .description(
+                        "Document metadata, containing the version,"
+                                + " original size & density, capabilities mask")
                 .field(INT, "MAJOR_VERSION", "Major version")
                 .field(INT, "MINOR_VERSION", "Minor version")
                 .field(INT, "PATCH_VERSION", "Patch version")
@@ -142,5 +165,4 @@
                 // .field(FLOAT, "DENSITY", "Major version")
                 .field(LONG, "CAPABILITIES", "Major version");
     }
-
 }
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/IntegerExpression.java b/core/java/com/android/internal/widget/remotecompose/core/operations/IntegerExpression.java
index 11730f3..c9a8508 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/IntegerExpression.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/IntegerExpression.java
@@ -15,8 +15,8 @@
  */
 package com.android.internal.widget.remotecompose.core.operations;
 
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT_ARRAY;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT_ARRAY;
 
 import com.android.internal.widget.remotecompose.core.Operation;
 import com.android.internal.widget.remotecompose.core.Operations;
@@ -24,17 +24,16 @@
 import com.android.internal.widget.remotecompose.core.VariableSupport;
 import com.android.internal.widget.remotecompose.core.WireBuffer;
 import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
 import com.android.internal.widget.remotecompose.core.operations.utilities.IntegerExpressionEvaluator;
 
 import java.util.Arrays;
 import java.util.List;
 
 /**
- * Operation to deal with AnimatedFloats
- * This is designed to be an optimized calculation for things like
- * injecting the width of the component int draw rect
- * As well as supporting generalized animation floats.
- * The floats represent a RPN style calculator
+ * Operation to deal with AnimatedFloats This is designed to be an optimized calculation for things
+ * like injecting the width of the component int draw rect As well as supporting generalized
+ * animation floats. The floats represent a RPN style calculator
  */
 public class IntegerExpression implements Operation, VariableSupport {
     private static final int OP_CODE = Operations.INTEGER_EXPRESSION;
@@ -45,7 +44,7 @@
     public int[] mSrcValue;
     public int[] mPreCalcValue;
     private float mLastChange = Float.NaN;
-    public static final int MAX_STRING_SIZE = 4000;
+    public static final int MAX_SIZE = 320;
     IntegerExpressionEvaluator mExp = new IntegerExpressionEvaluator();
 
     public IntegerExpression(int id, int mask, int[] value) {
@@ -70,7 +69,6 @@
         }
     }
 
-
     @Override
     public void registerListening(RemoteContext context) {
         for (int i = 0; i < mSrcValue.length; i++) {
@@ -91,6 +89,21 @@
         context.loadInteger(mId, v);
     }
 
+    /**
+     * Evaluate the expression
+     *
+     * @param context current context
+     * @return the resulting value
+     */
+    public int evaluate(RemoteContext context) {
+        updateVariables(context);
+        float t = context.getAnimationTime();
+        if (Float.isNaN(mLastChange)) {
+            mLastChange = t;
+        }
+        return mExp.eval(mPreMask, Arrays.copyOf(mPreCalcValue, mPreCalcValue.length));
+    }
+
     @Override
     public void write(WireBuffer buffer) {
         apply(buffer, mId, mMask, mSrcValue);
@@ -99,6 +112,9 @@
     @Override
     public String toString() {
         StringBuilder s = new StringBuilder();
+        if (mPreCalcValue == null) {
+            return "";
+        }
         for (int i = 0; i < mPreCalcValue.length; i++) {
             if (i != 0) {
                 s.append(" ");
@@ -128,9 +144,9 @@
      * Writes out the operation to the buffer
      *
      * @param buffer buffer to write to
-     * @param id     the id of the integer
-     * @param mask   the mask bits of ints & operators or variables
-     * @param value  array of integers to be evaluated
+     * @param id the id of the integer
+     * @param mask the mask bits of ints & operators or variables
+     * @param value array of integers to be evaluated
      */
     public static void apply(WireBuffer buffer, int id, int mask, int[] value) {
         buffer.start(OP_CODE);
@@ -146,7 +162,9 @@
         int id = buffer.readInt();
         int mask = buffer.readInt();
         int len = buffer.readInt();
-
+        if (len > MAX_SIZE) {
+            throw new RuntimeException("buffer corrupt integer expression " + len);
+        }
         int[] values = new int[len];
         for (int i = 0; i < values.length; i++) {
             values[i] = buffer.readInt();
@@ -156,17 +174,12 @@
     }
 
     public static void documentation(DocumentationBuilder doc) {
-        doc.operation("Data Operations",
-                        OP_CODE,
-                        CLASS_NAME)
+        doc.operation("Data Operations", OP_CODE, CLASS_NAME)
                 .description("Expression that computes an integer")
-                .field(INT, "id", "id of integer")
-                .field(INT, "mask",
-                        "bits representing operators or other id's")
-                .field(INT, "length",
-                        "length of array")
-                .field(INT_ARRAY, "values", "length",
-                        "Array of ints");
+                .field(DocumentedOperation.INT, "id", "id of integer")
+                .field(INT, "mask", "bits representing operators or other id's")
+                .field(INT, "length", "length of array")
+                .field(INT_ARRAY, "values", "length", "Array of ints");
     }
 
     @Override
@@ -177,8 +190,8 @@
     /**
      * given the "i" position in the mask is this an ID
      *
-     * @param mask  32 bit mask used for defining numbers vs other
-     * @param i     the bit in question
+     * @param mask 32 bit mask used for defining numbers vs other
+     * @param i the bit in question
      * @param value the value
      * @return true if this is an ID
      */
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixRestore.java b/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixRestore.java
index f3f9a51..04f8a50 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixRestore.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixRestore.java
@@ -27,15 +27,14 @@
 public class MatrixRestore extends PaintOperation {
     private static final int OP_CODE = Operations.MATRIX_RESTORE;
     private static final String CLASS_NAME = "MatrixRestore";
-    public MatrixRestore() {
-    }
+
+    public MatrixRestore() {}
 
     @Override
     public void write(WireBuffer buffer) {
         apply(buffer);
     }
 
-
     public static void read(WireBuffer buffer, List<Operation> operations) {
         MatrixRestore op = new MatrixRestore();
         operations.add(op);
@@ -46,12 +45,10 @@
         return "MatrixRestore";
     }
 
-
     public static String name() {
         return CLASS_NAME;
     }
 
-
     public static int id() {
         return OP_CODE;
     }
@@ -61,13 +58,10 @@
     }
 
     public static void documentation(DocumentationBuilder doc) {
-        doc.operation("Canvas Operations",
-                        OP_CODE,
-                        CLASS_NAME)
+        doc.operation("Canvas Operations", OP_CODE, CLASS_NAME)
                 .description("Restore the matrix and clip");
     }
 
-
     @Override
     public void paint(PaintContext context) {
         context.matrixRestore();
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixRotate.java b/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixRotate.java
index 9cc82fc..df10f32 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixRotate.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixRotate.java
@@ -15,13 +15,12 @@
  */
 package com.android.internal.widget.remotecompose.core.operations;
 
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.FLOAT;
-
 import com.android.internal.widget.remotecompose.core.Operation;
 import com.android.internal.widget.remotecompose.core.Operations;
 import com.android.internal.widget.remotecompose.core.PaintContext;
 import com.android.internal.widget.remotecompose.core.WireBuffer;
 import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
 
 import java.util.List;
 
@@ -30,12 +29,13 @@
     private static final String CLASS_NAME = "MatrixRotate";
 
     public static void read(WireBuffer buffer, List<Operation> operations) {
-        Maker m = new Maker() {
-            @Override
-            public DrawBase3 create(float v1, float v2, float v3) {
-                return new MatrixRotate(v1, v2, v3);
-            }
-        };
+        Maker m =
+                new Maker() {
+                    @Override
+                    public DrawBase3 create(float v1, float v2, float v3) {
+                        return new MatrixRotate(v1, v2, v3);
+                    }
+                };
         read(m, buffer, operations);
     }
 
@@ -48,20 +48,15 @@
     }
 
     public static void documentation(DocumentationBuilder doc) {
-        doc.operation("Canvas Operations",
-                        OP_CODE,
-                        CLASS_NAME)
+        doc.operation("Canvas Operations", OP_CODE, CLASS_NAME)
                 .description("apply rotation to matrix")
-                .field(FLOAT, "rotate", "Angle to rotate")
-                .field(FLOAT, "pivotX", "X Pivot point")
-                .field(FLOAT, "pivotY", "Y Pivot point");
+                .field(DocumentedOperation.FLOAT, "rotate", "Angle to rotate")
+                .field(DocumentedOperation.FLOAT, "pivotX", "X Pivot point")
+                .field(DocumentedOperation.FLOAT, "pivotY", "Y Pivot point");
     }
 
-
-    protected void write(WireBuffer buffer,
-                         float v1,
-                         float v2,
-                         float v3) {
+    @Override
+    protected void write(WireBuffer buffer, float v1, float v2, float v3) {
         apply(buffer, v1, v2, v3);
     }
 
@@ -83,10 +78,7 @@
      * @param y1 X Pivot point
      * @param x2 Y Pivot point
      */
-    public static void apply(WireBuffer buffer,
-                             float x1,
-                             float y1,
-                             float x2) {
+    public static void apply(WireBuffer buffer, float x1, float y1, float x2) {
         buffer.start(OP_CODE);
         buffer.writeFloat(x1);
         buffer.writeFloat(y1);
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixSave.java b/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixSave.java
index a47ed6b..67612c7 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixSave.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixSave.java
@@ -47,7 +47,6 @@
         return CLASS_NAME;
     }
 
-
     public static int id() {
         return OP_CODE;
     }
@@ -57,9 +56,7 @@
     }
 
     public static void documentation(DocumentationBuilder doc) {
-        doc.operation("Canvas Operations",
-                        OP_CODE,
-                        CLASS_NAME)
+        doc.operation("Canvas Operations", OP_CODE, CLASS_NAME)
                 .description("Save the matrix and clip to a stack");
     }
 
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixScale.java b/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixScale.java
index 769e798..26c898a 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixScale.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixScale.java
@@ -15,13 +15,12 @@
  */
 package com.android.internal.widget.remotecompose.core.operations;
 
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.FLOAT;
-
 import com.android.internal.widget.remotecompose.core.Operation;
 import com.android.internal.widget.remotecompose.core.Operations;
 import com.android.internal.widget.remotecompose.core.PaintContext;
 import com.android.internal.widget.remotecompose.core.WireBuffer;
 import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
 
 import java.util.List;
 
@@ -42,28 +41,18 @@
         return CLASS_NAME;
     }
 
-
-    protected void write(WireBuffer buffer,
-                         float v1,
-                         float v2,
-                         float v3,
-                         float v4) {
+    @Override
+    protected void write(WireBuffer buffer, float v1, float v2, float v3, float v4) {
         apply(buffer, v1, v2, v3, v4);
     }
 
     public static void documentation(DocumentationBuilder doc) {
-        doc.operation("Canvas Operations",
-                        OP_CODE,
-                        CLASS_NAME)
+        doc.operation("Canvas Operations", OP_CODE, CLASS_NAME)
                 .description("Draw the specified Oval")
-                .field(FLOAT, "scaleX",
-                        "The amount to scale in X")
-                .field(FLOAT, "scaleY",
-                        "The amount to scale in Y")
-                .field(FLOAT, "pivotX",
-                        "The x-coordinate for the pivot point")
-                .field(FLOAT, "pivotY",
-                        "The y-coordinate for the pivot point");
+                .field(DocumentedOperation.FLOAT, "scaleX", "The amount to scale in X")
+                .field(DocumentedOperation.FLOAT, "scaleY", "The amount to scale in Y")
+                .field(DocumentedOperation.FLOAT, "pivotX", "The x-coordinate for the pivot point")
+                .field(DocumentedOperation.FLOAT, "pivotY", "The y-coordinate for the pivot point");
     }
 
     public MatrixScale(float scaleX, float scaleY, float centerX, float centerY) {
@@ -80,16 +69,12 @@
      * Writes out the DrawOval to the buffer
      *
      * @param buffer buffer to write to
-     * @param x1     start x of DrawOval
-     * @param y1     start y of the DrawOval
-     * @param x2     end x of the DrawOval
-     * @param y2     end y of the DrawOval
+     * @param x1 start x of DrawOval
+     * @param y1 start y of the DrawOval
+     * @param x2 end x of the DrawOval
+     * @param y2 end y of the DrawOval
      */
-    public static void apply(WireBuffer buffer,
-                             float x1,
-                             float y1,
-                             float x2,
-                             float y2) {
+    public static void apply(WireBuffer buffer, float x1, float y1, float x2, float y2) {
         write(buffer, OP_CODE, x1, y1, x2, y2);
     }
 }
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixSkew.java b/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixSkew.java
index 34f71b4..d641178 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixSkew.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixSkew.java
@@ -15,7 +15,7 @@
  */
 package com.android.internal.widget.remotecompose.core.operations;
 
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.FLOAT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.FLOAT;
 
 import com.android.internal.widget.remotecompose.core.Operation;
 import com.android.internal.widget.remotecompose.core.Operations;
@@ -29,7 +29,6 @@
     public static final int OP_CODE = Operations.MATRIX_SKEW;
     public static final String CLASS_NAME = "MatrixSkew";
 
-
     public static void read(WireBuffer buffer, List<Operation> operations) {
         Maker m = MatrixSkew::new;
         read(m, buffer, operations);
@@ -43,25 +42,18 @@
         return CLASS_NAME;
     }
 
-
-    protected void write(WireBuffer buffer,
-                         float v1,
-                         float v2) {
+    @Override
+    protected void write(WireBuffer buffer, float v1, float v2) {
         apply(buffer, v1, v2);
     }
 
     public static void documentation(DocumentationBuilder doc) {
-        doc.operation("Canvas Operations",
-                        OP_CODE,
-                        CLASS_NAME)
+        doc.operation("Canvas Operations", OP_CODE, CLASS_NAME)
                 .description("Current matrix with the specified skew.")
-                .field(FLOAT, "skewX",
-                        "The amount to skew in X")
-                .field(FLOAT, "skewY",
-                        "The amount to skew in Y");
+                .field(FLOAT, "skewX", "The amount to skew in X")
+                .field(FLOAT, "skewY", "The amount to skew in Y");
     }
 
-
     public MatrixSkew(float skewX, float skewY) {
         super(skewX, skewY);
         mName = CLASS_NAME;
@@ -76,13 +68,10 @@
      * Writes out the DrawOval to the buffer
      *
      * @param buffer buffer to write to
-     * @param x1     start x of DrawOval
-     * @param y1     start y of the DrawOval
+     * @param x1 start x of DrawOval
+     * @param y1 start y of the DrawOval
      */
-    public static void apply(WireBuffer buffer,
-                             float x1,
-                             float y1
-    ) {
+    public static void apply(WireBuffer buffer, float x1, float y1) {
         write(buffer, OP_CODE, x1, y1);
     }
 }
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixTranslate.java b/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixTranslate.java
index 8561343..e008292 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixTranslate.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixTranslate.java
@@ -15,13 +15,12 @@
  */
 package com.android.internal.widget.remotecompose.core.operations;
 
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.FLOAT;
-
 import com.android.internal.widget.remotecompose.core.Operation;
 import com.android.internal.widget.remotecompose.core.Operations;
 import com.android.internal.widget.remotecompose.core.PaintContext;
 import com.android.internal.widget.remotecompose.core.WireBuffer;
 import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
 
 import java.util.List;
 
@@ -42,22 +41,16 @@
         return CLASS_NAME;
     }
 
-    protected void write(WireBuffer buffer,
-                         float v1,
-                         float v2) {
+    @Override
+    protected void write(WireBuffer buffer, float v1, float v2) {
         apply(buffer, v1, v2);
     }
 
     public static void documentation(DocumentationBuilder doc) {
-        doc.operation("Canvas Operations",
-                        OP_CODE,
-                        "MatrixTranslate")
+        doc.operation("Canvas Operations", OP_CODE, "MatrixTranslate")
                 .description("Preconcat the current matrix with the specified translation")
-                .field(FLOAT, "dx",
-                        "The distance to translate in X")
-                .field(FLOAT, "dy",
-                        "The distance to translate in Y");
-
+                .field(DocumentedOperation.FLOAT, "dx", "The distance to translate in X")
+                .field(DocumentedOperation.FLOAT, "dy", "The distance to translate in Y");
     }
 
     public MatrixTranslate(float translateX, float translateY) {
@@ -74,12 +67,10 @@
      * Writes out the DrawOval to the buffer
      *
      * @param buffer buffer to write to
-     * @param x1     start x of DrawOval
-     * @param y1     start y of the DrawOval
+     * @param x1 start x of DrawOval
+     * @param y1 start y of the DrawOval
      */
-    public static void apply(WireBuffer buffer,
-                             float x1,
-                             float y1) {
+    public static void apply(WireBuffer buffer, float x1, float y1) {
         write(buffer, OP_CODE, x1, y1);
     }
 }
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/NamedVariable.java b/core/java/com/android/internal/widget/remotecompose/core/operations/NamedVariable.java
index 2cf83cd..fa6e271 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/NamedVariable.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/NamedVariable.java
@@ -15,20 +15,19 @@
  */
 package com.android.internal.widget.remotecompose.core.operations;
 
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.UTF8;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.UTF8;
 
 import com.android.internal.widget.remotecompose.core.Operation;
 import com.android.internal.widget.remotecompose.core.Operations;
 import com.android.internal.widget.remotecompose.core.RemoteContext;
 import com.android.internal.widget.remotecompose.core.WireBuffer;
 import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
 
 import java.util.List;
 
-/**
- * Operation to deal with Text data
- */
+/** Operation to deal with Text data */
 public class NamedVariable implements Operation {
     private static final int OP_CODE = Operations.NAMED_VARIABLE;
     private static final String CLASS_NAME = "NamedVariable";
@@ -39,6 +38,7 @@
     public static final int COLOR_TYPE = 2;
     public static final int FLOAT_TYPE = 1;
     public static final int STRING_TYPE = 0;
+    public static final int IMAGE_TYPE = 3;
 
     public NamedVariable(int varId, int varType, String name) {
         this.mVarId = varId;
@@ -53,8 +53,12 @@
 
     @Override
     public String toString() {
-        return "VariableName[" + mVarId + "] = \""
-                + Utils.trimString(mVarName, 10) + "\" type=" + mVarType;
+        return "VariableName["
+                + mVarId
+                + "] = \""
+                + Utils.trimString(mVarName, 10)
+                + "\" type="
+                + mVarType;
     }
 
     public static String name() {
@@ -80,7 +84,7 @@
         buffer.writeUTF8(text);
     }
 
-    public static  void read(WireBuffer buffer, List<Operation> operations) {
+    public static void read(WireBuffer buffer, List<Operation> operations) {
         int varId = buffer.readInt();
         int varType = buffer.readInt();
         String text = buffer.readUTF8(MAX_STRING_SIZE);
@@ -88,11 +92,9 @@
     }
 
     public static void documentation(DocumentationBuilder doc) {
-        doc.operation("Data Operations",
-                        OP_CODE,
-                        CLASS_NAME)
+        doc.operation("Data Operations", OP_CODE, CLASS_NAME)
                 .description("Add a string name for an ID")
-                .field(INT, "varId", "id to label")
+                .field(DocumentedOperation.INT, "varId", "id to label")
                 .field(INT, "varType", "The type of variable")
                 .field(UTF8, "name", "String");
     }
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/PaintData.java b/core/java/com/android/internal/widget/remotecompose/core/operations/PaintData.java
index ae7a892..095a010 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/PaintData.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/PaintData.java
@@ -15,8 +15,8 @@
  */
 package com.android.internal.widget.remotecompose.core.operations;
 
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT_ARRAY;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT_ARRAY;
 
 import com.android.internal.widget.remotecompose.core.Operation;
 import com.android.internal.widget.remotecompose.core.Operations;
@@ -36,8 +36,7 @@
     public PaintBundle mPaintData = new PaintBundle();
     public static final int MAX_STRING_SIZE = 4000;
 
-    public PaintData() {
-    }
+    public PaintData() {}
 
     @Override
     public void updateVariables(RemoteContext context) {
@@ -59,7 +58,6 @@
         return "PaintData " + "\"" + mPaintData + "\"";
     }
 
-
     public static String name() {
         return CLASS_NAME;
     }
@@ -80,13 +78,10 @@
     }
 
     public static void documentation(DocumentationBuilder doc) {
-        doc.operation("Data Operations",
-                        OP_CODE,
-                        CLASS_NAME)
+        doc.operation("Data Operations", OP_CODE, CLASS_NAME)
                 .description("Encode a Paint ")
                 .field(INT, "length", "id string")
-                .field(INT_ARRAY, "paint", "length",
-                        "path encoded as floats");
+                .field(INT_ARRAY, "paint", "length", "path encoded as floats");
     }
 
     @Override
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/PathData.java b/core/java/com/android/internal/widget/remotecompose/core/operations/PathData.java
index 91352d9..13d5a49 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/PathData.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/PathData.java
@@ -15,8 +15,8 @@
  */
 package com.android.internal.widget.remotecompose.core.operations;
 
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.FLOAT_ARRAY;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.FLOAT_ARRAY;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
 
 import com.android.internal.widget.remotecompose.core.Operation;
 import com.android.internal.widget.remotecompose.core.Operations;
@@ -24,6 +24,7 @@
 import com.android.internal.widget.remotecompose.core.VariableSupport;
 import com.android.internal.widget.remotecompose.core.WireBuffer;
 import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
 
 import java.util.Arrays;
 import java.util.List;
@@ -46,8 +47,7 @@
         for (int i = 0; i < mFloatPath.length; i++) {
             float v = mFloatPath[i];
             if (Utils.isVariable(v)) {
-                mOutputPath[i] = (Float.isNaN(v))
-                        ? context.getFloat(Utils.idFromNan(v)) : v;
+                mOutputPath[i] = Float.isNaN(v) ? context.getFloat(Utils.idFromNan(v)) : v;
             } else {
                 mOutputPath[i] = v;
             }
@@ -79,30 +79,15 @@
     }
 
     /**
-     * public float[] getFloatPath(PaintContext context) {
-     * float[] ret = mRetFloats; // Assume retFloats is declared elsewhere
-     * if (ret == null) {
-     * return mFloatPath; // Assume floatPath is declared elsewhere
-     * }
-     * float[] localRef = mRef; // Assume ref is of type Float[]
-     * if (localRef == null) {
-     * for (int i = 0; i < mFloatPath.length; i++) {
-     * ret[i] = mFloatPath[i];
-     * }
-     * } else {
-     * for (int i = 0; i < mFloatPath.length; i++) {
-     * float lr = localRef[i];
-     * if (Float.isNaN(lr)) {
-     * ret[i] = Utils.getActualValue(lr);
-     * } else {
-     * ret[i] = mFloatPath[i];
-     * }
-     * }
-     * }
-     * return ret;
-     * }
+     * public float[] getFloatPath(PaintContext context) { float[] ret = mRetFloats; // Assume
+     * retFloats is declared elsewhere if (ret == null) { return mFloatPath; // Assume floatPath is
+     * declared elsewhere } float[] localRef = mRef; // Assume ref is of type Float[] if (localRef
+     * == null) { for (int i = 0; i < mFloatPath.length; i++) { ret[i] = mFloatPath[i]; } } else {
+     * for (int i = 0; i < mFloatPath.length; i++) { float lr = localRef[i]; if (Float.isNaN(lr)) {
+     * ret[i] = Utils.getActualValue(lr); } else { ret[i] = mFloatPath[i]; } } } return ret; }
      */
     public static final int MOVE = 10;
+
     public static final int LINE = 11;
     public static final int QUADRATIC = 12;
     public static final int CONIC = 13;
@@ -145,17 +130,13 @@
     }
 
     public static void documentation(DocumentationBuilder doc) {
-        doc.operation("Data Operations",
-                        OP_CODE,
-                        CLASS_NAME)
+        doc.operation("Data Operations", OP_CODE, CLASS_NAME)
                 .description("Encode a Path ")
-                .field(INT, "id", "id string")
+                .field(DocumentedOperation.INT, "id", "id string")
                 .field(INT, "length", "id string")
-                .field(FLOAT_ARRAY, "pathData", "length",
-                        "path encoded as floats");
+                .field(FLOAT_ARRAY, "pathData", "length", "path encoded as floats");
     }
 
-
     public static String pathString(float[] path) {
         if (path == null) {
             return "null";
@@ -208,5 +189,4 @@
     public void apply(RemoteContext context) {
         context.loadPathData(mInstanceId, mOutputPath);
     }
-
 }
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/RootContentBehavior.java b/core/java/com/android/internal/widget/remotecompose/core/operations/RootContentBehavior.java
index 33f997f..4a8f532 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/RootContentBehavior.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/RootContentBehavior.java
@@ -15,22 +15,21 @@
  */
 package com.android.internal.widget.remotecompose.core.operations;
 
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
-
 import com.android.internal.widget.remotecompose.core.Operation;
 import com.android.internal.widget.remotecompose.core.Operations;
 import com.android.internal.widget.remotecompose.core.RemoteComposeOperation;
 import com.android.internal.widget.remotecompose.core.RemoteContext;
 import com.android.internal.widget.remotecompose.core.WireBuffer;
 import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
 
 import java.util.List;
 
 /**
  * Describe some basic information for a RemoteCompose document
- * <p>
- * It encodes the version of the document (following semantic versioning) as well
- * as the dimensions of the document in pixels.
+ *
+ * <p>It encodes the version of the document (following semantic versioning) as well as the
+ * dimensions of the document in pixels.
  */
 public class RootContentBehavior implements RemoteComposeOperation {
     private static final int OP_CODE = Operations.ROOT_CONTENT_BEHAVIOR;
@@ -67,8 +66,8 @@
     public static final int ALIGNMENT_START = 16;
     public static final int ALIGNMENT_HORIZONTAL_CENTER = 32;
     public static final int ALIGNMENT_END = 64;
-    public static final int ALIGNMENT_CENTER = ALIGNMENT_HORIZONTAL_CENTER
-            + ALIGNMENT_VERTICAL_CENTER;
+    public static final int ALIGNMENT_CENTER =
+            ALIGNMENT_HORIZONTAL_CENTER + ALIGNMENT_VERTICAL_CENTER;
 
     ///////////////////////////////////////////////////////////////////////////////////////////////
     // Layout mode
@@ -98,21 +97,14 @@
     /**
      * Sets the way the player handles the content
      *
-     * @param scroll    set the horizontal behavior (NONE|SCROLL_HORIZONTAL|SCROLL_VERTICAL)
+     * @param scroll set the horizontal behavior (NONE|SCROLL_HORIZONTAL|SCROLL_VERTICAL)
      * @param alignment set the alignment of the content (TOP|CENTER|BOTTOM|START|END)
-     * @param sizing    set the type of sizing for the content (NONE|SIZING_LAYOUT|SIZING_SCALE)
-     * @param mode      set the mode of sizing, either LAYOUT modes or SCALE modes
-     *                  the LAYOUT modes are:
-     *                  - LAYOUT_MATCH_PARENT
-     *                  - LAYOUT_WRAP_CONTENT
-     *                  or adding an horizontal mode and a vertical mode:
-     *                  - LAYOUT_HORIZONTAL_MATCH_PARENT
-     *                  - LAYOUT_HORIZONTAL_WRAP_CONTENT
-     *                  - LAYOUT_HORIZONTAL_FIXED
-     *                  - LAYOUT_VERTICAL_MATCH_PARENT
-     *                  - LAYOUT_VERTICAL_WRAP_CONTENT
-     *                  - LAYOUT_VERTICAL_FIXED
-     *                  The LAYOUT_*_FIXED modes will use the intrinsic document size
+     * @param sizing set the type of sizing for the content (NONE|SIZING_LAYOUT|SIZING_SCALE)
+     * @param mode set the mode of sizing, either LAYOUT modes or SCALE modes the LAYOUT modes are:
+     *     - LAYOUT_MATCH_PARENT - LAYOUT_WRAP_CONTENT or adding an horizontal mode and a vertical
+     *     mode: - LAYOUT_HORIZONTAL_MATCH_PARENT - LAYOUT_HORIZONTAL_WRAP_CONTENT -
+     *     LAYOUT_HORIZONTAL_FIXED - LAYOUT_VERTICAL_MATCH_PARENT - LAYOUT_VERTICAL_WRAP_CONTENT -
+     *     LAYOUT_VERTICAL_FIXED The LAYOUT_*_FIXED modes will use the intrinsic document size
      */
     public RootContentBehavior(int scroll, int alignment, int sizing, int mode) {
         switch (scroll) {
@@ -121,41 +113,43 @@
             case SCROLL_VERTICAL:
                 mScroll = scroll;
                 break;
-            default: {
+            default:
                 System.out.println(TAG + "incorrect scroll value " + scroll);
-            }
         }
         if (alignment == ALIGNMENT_CENTER) {
             mAlignment = alignment;
         } else {
             int horizontalContentAlignment = alignment & 0xF0;
             int verticalContentAlignment = alignment & 0xF;
-            boolean validHorizontalAlignment = horizontalContentAlignment == ALIGNMENT_START
-                    || horizontalContentAlignment == ALIGNMENT_HORIZONTAL_CENTER
-                    || horizontalContentAlignment == ALIGNMENT_END;
-            boolean validVerticalAlignment = verticalContentAlignment == ALIGNMENT_TOP
-                    || verticalContentAlignment == ALIGNMENT_VERTICAL_CENTER
-                    || verticalContentAlignment == ALIGNMENT_BOTTOM;
+            boolean validHorizontalAlignment =
+                    horizontalContentAlignment == ALIGNMENT_START
+                            || horizontalContentAlignment == ALIGNMENT_HORIZONTAL_CENTER
+                            || horizontalContentAlignment == ALIGNMENT_END;
+            boolean validVerticalAlignment =
+                    verticalContentAlignment == ALIGNMENT_TOP
+                            || verticalContentAlignment == ALIGNMENT_VERTICAL_CENTER
+                            || verticalContentAlignment == ALIGNMENT_BOTTOM;
             if (validHorizontalAlignment && validVerticalAlignment) {
                 mAlignment = alignment;
             } else {
-                System.out.println(TAG + "incorrect alignment "
-                        + " h: " + horizontalContentAlignment
-                        + " v: " + verticalContentAlignment);
+                System.out.println(
+                        TAG
+                                + "incorrect alignment "
+                                + " h: "
+                                + horizontalContentAlignment
+                                + " v: "
+                                + verticalContentAlignment);
             }
         }
         switch (sizing) {
-            case SIZING_LAYOUT: {
+            case SIZING_LAYOUT:
                 System.out.println(TAG + "sizing_layout is not yet supported");
-            }
-            break;
-            case SIZING_SCALE: {
+                break;
+            case SIZING_SCALE:
                 mSizing = sizing;
-            }
-            break;
-            default: {
+                break;
+            default:
                 System.out.println(TAG + "incorrect sizing value " + sizing);
-            }
         }
         if (mSizing == SIZING_LAYOUT) {
             if (mode != NONE) {
@@ -171,9 +165,8 @@
                 case SCALE_FILL_BOUNDS:
                     mMode = mode;
                     break;
-                default: {
+                default:
                     System.out.println(TAG + "incorrect mode for scale sizing, mode: " + mode);
-                }
             }
         }
     }
@@ -185,8 +178,12 @@
 
     @Override
     public String toString() {
-        return "ROOT_CONTENT_BEHAVIOR scroll: " + mScroll
-                + " sizing: " + mSizing + " mode: " + mMode;
+        return "ROOT_CONTENT_BEHAVIOR scroll: "
+                + mScroll
+                + " sizing: "
+                + mSizing
+                + " mode: "
+                + mMode;
     }
 
     @Override
@@ -199,12 +196,10 @@
         return toString();
     }
 
-
     public static String name() {
         return CLASS_NAME;
     }
 
-
     public static int id() {
         return OP_CODE;
     }
@@ -217,7 +212,6 @@
         buffer.writeInt(mode);
     }
 
-
     public static void read(WireBuffer buffer, List<Operation> operations) {
         int scroll = buffer.readInt();
         int alignment = buffer.readInt();
@@ -228,30 +222,27 @@
         operations.add(rootContentBehavior);
     }
 
-
     public static void documentation(DocumentationBuilder doc) {
-        doc.operation("Protocol Operations",
-                        OP_CODE,
-                        CLASS_NAME)
+        doc.operation("Protocol Operations", OP_CODE, CLASS_NAME)
                 .description("Describes the behaviour of the root")
-                .field(INT, "scroll", "scroll")
+                .field(DocumentedOperation.INT, "scroll", "scroll")
                 .possibleValues("SCROLL_HORIZONTAL", SCROLL_HORIZONTAL)
                 .possibleValues("SCROLL_VERTICAL", SCROLL_VERTICAL)
-                .field(INT, "alignment", "alignment")
+                .field(DocumentedOperation.INT, "alignment", "alignment")
                 .possibleValues("ALIGNMENT_TOP", ALIGNMENT_TOP)
                 .possibleValues("ALIGNMENT_VERTICAL_CENTER", ALIGNMENT_VERTICAL_CENTER)
                 .possibleValues("ALIGNMENT_BOTTOM", ALIGNMENT_BOTTOM)
                 .possibleValues("ALIGNMENT_START", ALIGNMENT_START)
                 .possibleValues("ALIGNMENT_START", ALIGNMENT_START)
                 .possibleValues("ALIGNMENT_END", ALIGNMENT_END)
-                .field(INT, "sizing", "sizing")
+                .field(DocumentedOperation.INT, "sizing", "sizing")
                 .possibleValues("SCALE_INSIDE", SCALE_INSIDE)
                 .possibleValues("SCALE_FIT", SCALE_FIT)
                 .possibleValues("SCALE_FILL_WIDTH", SCALE_FILL_WIDTH)
                 .possibleValues("SCALE_FILL_HEIGHT", SCALE_FILL_HEIGHT)
                 .possibleValues("SCALE_CROP", SCALE_CROP)
                 .possibleValues("SCALE_FILL_BOUNDS", SCALE_FILL_BOUNDS)
-                .field(INT, "mode", "mode")
+                .field(DocumentedOperation.INT, "mode", "mode")
                 .possibleValues("LAYOUT_HORIZONTAL_MATCH_PARENT", LAYOUT_HORIZONTAL_MATCH_PARENT)
                 .possibleValues("LAYOUT_HORIZONTAL_WRAP_CONTENT", LAYOUT_HORIZONTAL_WRAP_CONTENT)
                 .possibleValues("LAYOUT_HORIZONTAL_FIXED", LAYOUT_HORIZONTAL_FIXED)
@@ -260,6 +251,5 @@
                 .possibleValues("LAYOUT_VERTICAL_FIXED", LAYOUT_VERTICAL_FIXED)
                 .possibleValues("LAYOUT_MATCH_PARENT", LAYOUT_MATCH_PARENT)
                 .possibleValues("LAYOUT_WRAP_CONTENT", LAYOUT_WRAP_CONTENT);
-
     }
 }
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/RootContentDescription.java b/core/java/com/android/internal/widget/remotecompose/core/operations/RootContentDescription.java
index e1533ee..bff9029 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/RootContentDescription.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/RootContentDescription.java
@@ -15,20 +15,17 @@
  */
 package com.android.internal.widget.remotecompose.core.operations;
 
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
-
 import com.android.internal.widget.remotecompose.core.Operation;
 import com.android.internal.widget.remotecompose.core.Operations;
 import com.android.internal.widget.remotecompose.core.RemoteComposeOperation;
 import com.android.internal.widget.remotecompose.core.RemoteContext;
 import com.android.internal.widget.remotecompose.core.WireBuffer;
 import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
 
 import java.util.List;
 
-/**
- * Describe a content description for the document
- */
+/** Describe a content description for the document */
 public class RootContentDescription implements RemoteComposeOperation {
     private static final int OP_CODE = Operations.ROOT_CONTENT_DESCRIPTION;
     private static final String CLASS_NAME = "RootContentDescription";
@@ -82,13 +79,9 @@
         operations.add(header);
     }
 
-
     public static void documentation(DocumentationBuilder doc) {
-        doc.operation("Protocol Operations",
-                        OP_CODE,
-                        CLASS_NAME)
+        doc.operation("Protocol Operations", OP_CODE, CLASS_NAME)
                 .description("Content description of root")
-                .field(INT, "id", "id of Int");
-
+                .field(DocumentedOperation.INT, "id", "id of Int");
     }
 }
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/ShaderData.java b/core/java/com/android/internal/widget/remotecompose/core/operations/ShaderData.java
index c4dde6e..7ec7879 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/ShaderData.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/ShaderData.java
@@ -15,12 +15,12 @@
  */
 package com.android.internal.widget.remotecompose.core.operations;
 
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.BYTE;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.FLOAT_ARRAY;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT_ARRAY;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.SHORT;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.UTF8;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.BYTE;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.FLOAT_ARRAY;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT_ARRAY;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.SHORT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.UTF8;
 
 import com.android.internal.widget.remotecompose.core.Operation;
 import com.android.internal.widget.remotecompose.core.Operations;
@@ -28,15 +28,15 @@
 import com.android.internal.widget.remotecompose.core.VariableSupport;
 import com.android.internal.widget.remotecompose.core.WireBuffer;
 import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
 
 import java.util.Arrays;
 import java.util.HashMap;
 import java.util.List;
 
 /**
- * Operation to deal with bitmap data
- * On getting an Image during a draw call the bitmap is compressed and saved
- * in playback the image is decompressed
+ * Operation to deal with bitmap data On getting an Image during a draw call the bitmap is
+ * compressed and saved in playback the image is decompressed
  */
 public class ShaderData implements Operation, VariableSupport {
     private static final int OP_CODE = Operations.DATA_SHADER;
@@ -48,11 +48,12 @@
     HashMap<String, int[]> mUniformIntMap = null;
     HashMap<String, Integer> mUniformBitmapMap = null;
 
-    public ShaderData(int shaderID,
-                      int shaderTextId,
-                      HashMap<String, float[]> floatMap,
-                      HashMap<String, int[]> intMap,
-                      HashMap<String, Integer> bitmapMap) {
+    public ShaderData(
+            int shaderID,
+            int shaderTextId,
+            HashMap<String, float[]> floatMap,
+            HashMap<String, int[]> intMap,
+            HashMap<String, Integer> bitmapMap) {
         mShaderID = shaderID;
         mShaderTextId = shaderTextId;
         if (floatMap != null) {
@@ -77,7 +78,6 @@
                 mUniformBitmapMap.put(name, bitmapMap.get(name));
             }
         }
-
     }
 
     public int getShaderTextId() {
@@ -107,7 +107,7 @@
     /**
      * get the name of all know uniform integers
      *
-     * @return  Name of all integer uniforms
+     * @return Name of all integer uniforms
      */
     public String[] getUniformIntegerNames() {
         if (mUniformIntMap == null) return new String[0];
@@ -146,8 +146,13 @@
 
     @Override
     public void write(WireBuffer buffer) {
-        apply(buffer, mShaderID, mShaderTextId,
-                mUniformFloatMap, mUniformIntMap, mUniformBitmapMap);
+        apply(
+                buffer,
+                mShaderID,
+                mShaderTextId,
+                mUniformFloatMap,
+                mUniformIntMap,
+                mUniformBitmapMap);
     }
 
     @Override
@@ -202,10 +207,13 @@
      * @param intMap the map of int uniforms
      * @param bitmapMap the map of bitmap uniforms
      */
-    public static void apply(WireBuffer buffer, int shaderID, int shaderTextId,
-                             HashMap<String, float[]> floatMap,
-                             HashMap<String, int[]> intMap,
-                             HashMap<String, Integer> bitmapMap) {
+    public static void apply(
+            WireBuffer buffer,
+            int shaderID,
+            int shaderTextId,
+            HashMap<String, float[]> floatMap,
+            HashMap<String, int[]> intMap,
+            HashMap<String, Integer> bitmapMap) {
         buffer.start(OP_CODE);
         buffer.writeInt(shaderID);
 
@@ -248,7 +256,6 @@
         }
     }
 
-
     public static void read(WireBuffer buffer, List<Operation> operations) {
         int shaderID = buffer.readInt();
         int shaderTextId = buffer.readInt();
@@ -298,16 +305,13 @@
                 bitmapMap.put(name, val);
             }
         }
-        operations.add(new ShaderData(shaderID, shaderTextId,
-                floatMap, intMap, bitmapMap));
+        operations.add(new ShaderData(shaderID, shaderTextId, floatMap, intMap, bitmapMap));
     }
 
     public static void documentation(DocumentationBuilder doc) {
-        doc.operation("Data Operations",
-                        OP_CODE,
-                        CLASS_NAME)
+        doc.operation("Data Operations", OP_CODE, CLASS_NAME)
                 .description("Shader")
-                .field(INT, "shaderID", "id of shader")
+                .field(DocumentedOperation.INT, "shaderID", "id of shader")
                 .field(BYTE, " floatSize", "number of float uniforms")
                 .field(BYTE, " intSize", "number of int uniform")
                 .field(SHORT, " intSize", "number of int uniform")
@@ -319,7 +323,6 @@
                 .field(INT_ARRAY, "VALUE", "int uniform (max 4)")
                 .field(UTF8, "bitmapName", "name of bitmap")
                 .field(INT, "VALUE", "id of bitmap");
-
     }
 
     @Override
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/TextData.java b/core/java/com/android/internal/widget/remotecompose/core/operations/TextData.java
index b49cb76..6383249 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/TextData.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/TextData.java
@@ -15,8 +15,7 @@
  */
 package com.android.internal.widget.remotecompose.core.operations;
 
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.UTF8;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.UTF8;
 
 import com.android.internal.widget.remotecompose.core.Operation;
 import com.android.internal.widget.remotecompose.core.Operations;
@@ -24,13 +23,12 @@
 import com.android.internal.widget.remotecompose.core.SerializableToString;
 import com.android.internal.widget.remotecompose.core.WireBuffer;
 import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
 import com.android.internal.widget.remotecompose.core.operations.utilities.StringSerializer;
 
 import java.util.List;
 
-/**
- * Operation to deal with Text data
- */
+/** Operation to deal with Text data */
 public class TextData implements Operation, SerializableToString {
     private static final int OP_CODE = Operations.DATA_TEXT;
     private static final String CLASS_NAME = "TextData";
@@ -50,15 +48,13 @@
 
     @Override
     public String toString() {
-        return "TextData[" + mTextId + "] = \""
-                + Utils.trimString(mText, 10) + "\"";
+        return "TextData[" + mTextId + "] = \"" + Utils.trimString(mText, 10) + "\"";
     }
 
     public static String name() {
         return CLASS_NAME;
     }
 
-
     public static int id() {
         return OP_CODE;
     }
@@ -69,7 +65,6 @@
         buffer.writeUTF8(text);
     }
 
-
     public static void read(WireBuffer buffer, List<Operation> operations) {
         int textId = buffer.readInt();
 
@@ -77,18 +72,13 @@
         operations.add(new TextData(textId, text));
     }
 
-
     public static void documentation(DocumentationBuilder doc) {
-        doc.operation("Data Operations",
-                        OP_CODE,
-                        CLASS_NAME)
+        doc.operation("Data Operations", OP_CODE, CLASS_NAME)
                 .description("Encode a string ")
-                .field(INT, "id", "id string")
-                .field(UTF8, "text",
-                        "encode text as a string");
+                .field(DocumentedOperation.INT, "id", "id string")
+                .field(UTF8, "text", "encode text as a string");
     }
 
-
     @Override
     public void apply(RemoteContext context) {
         context.loadText(mTextId, mText);
@@ -101,8 +91,7 @@
 
     @Override
     public void serializeToString(int indent, StringSerializer serializer) {
-        serializer.append(indent, getSerializedName() + "<" + mTextId
-                + "> = \"" + mText + "\"");
+        serializer.append(indent, getSerializedName() + "<" + mTextId + "> = \"" + mText + "\"");
     }
 
     private String getSerializedName() {
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/TextFromFloat.java b/core/java/com/android/internal/widget/remotecompose/core/operations/TextFromFloat.java
index 8f235dc..0d966d1 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/TextFromFloat.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/TextFromFloat.java
@@ -15,8 +15,8 @@
  */
 package com.android.internal.widget.remotecompose.core.operations;
 
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.SHORT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.SHORT;
 
 import com.android.internal.widget.remotecompose.core.Operation;
 import com.android.internal.widget.remotecompose.core.Operations;
@@ -24,14 +24,15 @@
 import com.android.internal.widget.remotecompose.core.VariableSupport;
 import com.android.internal.widget.remotecompose.core.WireBuffer;
 import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
 import com.android.internal.widget.remotecompose.core.operations.utilities.StringUtils;
 
 import java.util.List;
 
 /**
- * Operation convert floats to text
- * This command is structured [command][textID][before,after][flags]
- * before and after define number of digits before and after the decimal point
+ * Operation convert floats to text This command is structured
+ * [command][textID][before,after][flags] before and after define number of digits before and after
+ * the decimal point
  */
 public class TextFromFloat implements Operation, VariableSupport {
     private static final int OP_CODE = Operations.TEXT_FROM_FLOAT;
@@ -49,12 +50,12 @@
     public static final int PAD_AFTER_SPACE = 0; // pad past point with space
     public static final int PAD_AFTER_NONE = 1; // do not pad past last digit
     public static final int PAD_AFTER_ZERO = 3; // pad with 0 past last digit
-    public static final int PAD_PRE_SPACE = 0;  // pad before number with spaces
-    public static final int PAD_PRE_NONE = 4;   // pad before number with 0s
-    public static final int PAD_PRE_ZERO = 12;  // do not pad before number
+    public static final int PAD_PRE_SPACE = 0; // pad before number with spaces
+    public static final int PAD_PRE_NONE = 4; // pad before number with 0s
+    public static final int PAD_PRE_ZERO = 12; // do not pad before number
 
-    public TextFromFloat(int textId, float value, short digitsBefore,
-                         short digitsAfter, int flags) {
+    public TextFromFloat(
+            int textId, float value, short digitsBefore, short digitsAfter, int flags) {
         this.mTextId = textId;
         this.mValue = value;
         this.mDigitsAfter = digitsAfter;
@@ -87,17 +88,23 @@
 
     @Override
     public void write(WireBuffer buffer) {
-        apply(buffer, mTextId, mValue, mDigitsAfter, mDigitsBefore, mFlags);
+        apply(buffer, mTextId, mValue, mDigitsBefore, mDigitsAfter, mFlags);
     }
 
     @Override
     public String toString() {
-        return "TextFromFloat[" + mTextId + "] = "
-                + Utils.floatToString(mValue) + " " + mDigitsBefore
-                + "." + mDigitsAfter + " " + mFlags;
+        return "TextFromFloat["
+                + mTextId
+                + "] = "
+                + Utils.floatToString(mValue)
+                + " "
+                + mDigitsBefore
+                + "."
+                + mDigitsAfter
+                + " "
+                + mFlags;
     }
 
-
     @Override
     public void updateVariables(RemoteContext context) {
         if (Float.isNaN(mValue)) {
@@ -123,22 +130,25 @@
     /**
      * Writes out the operation to the buffer
      *
-     * @param buffer       buffer to write to
-     * @param textId       the id of the output text
-     * @param value        the float value to be turned into strings
+     * @param buffer buffer to write to
+     * @param textId the id of the output text
+     * @param value the float value to be turned into strings
      * @param digitsBefore the digits before the decimal point
-     * @param digitsAfter  the digits after the decimal point
-     * @param flags        flags that control if and how to fill the empty spots
+     * @param digitsAfter the digits after the decimal point
+     * @param flags flags that control if and how to fill the empty spots
      */
-    public static void apply(WireBuffer buffer, int textId,
-                             float value, short digitsBefore,
-                             short digitsAfter, int flags) {
+    public static void apply(
+            WireBuffer buffer,
+            int textId,
+            float value,
+            short digitsBefore,
+            short digitsAfter,
+            int flags) {
         buffer.start(OP_CODE);
         buffer.writeInt(textId);
         buffer.writeFloat(value);
         buffer.writeInt((digitsBefore << 16) | digitsAfter);
         buffer.writeInt(flags);
-
     }
 
     public static void read(WireBuffer buffer, List<Operation> operations) {
@@ -153,27 +163,19 @@
     }
 
     public static void documentation(DocumentationBuilder doc) {
-        doc.operation("Expressions Operations",
-                        OP_CODE,
-                        CLASS_NAME)
+        doc.operation("Expressions Operations", OP_CODE, CLASS_NAME)
                 .description("Draw text along path object")
-                .field(INT, "textId",
-                        "id of the text generated")
-                .field(INT, "value",
-                        "Value to add")
-                .field(SHORT, "prePoint",
-                        "digits before the decimal point")
-                .field(SHORT, "pstPoint",
-                        "digit after the decimal point")
+                .field(DocumentedOperation.INT, "textId", "id of the text generated")
+                .field(INT, "value", "Value to add")
+                .field(SHORT, "prePoint", "digits before the decimal point")
+                .field(SHORT, "pstPoint", "digit after the decimal point")
                 .field(INT, "flags", "options on padding");
     }
 
-
     @Override
     public void apply(RemoteContext context) {
         float v = mOutValue;
-        String s = StringUtils.floatToString(v, mDigitsBefore,
-                mDigitsAfter, mPre, mAfter);
+        String s = StringUtils.floatToString(v, mDigitsBefore, mDigitsAfter, mPre, mAfter);
         context.loadText(mTextId, s);
     }
 
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/TextLength.java b/core/java/com/android/internal/widget/remotecompose/core/operations/TextLength.java
new file mode 100644
index 0000000..e148fb9
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/TextLength.java
@@ -0,0 +1,93 @@
+/*
+ * 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.internal.widget.remotecompose.core.operations;
+
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
+
+import com.android.internal.widget.remotecompose.core.Operation;
+import com.android.internal.widget.remotecompose.core.Operations;
+import com.android.internal.widget.remotecompose.core.RemoteContext;
+import com.android.internal.widget.remotecompose.core.WireBuffer;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+
+import java.util.List;
+
+/** Operation to measure the length of the text */
+public class TextLength implements Operation {
+    private static final int OP_CODE = Operations.TEXT_LENGTH;
+    private static final String CLASS_NAME = "TextLength";
+    public int mLengthId;
+    public int mTextId;
+
+    public TextLength(int lengthId, int textId) {
+        this.mLengthId = lengthId;
+        this.mTextId = textId;
+    }
+
+    @Override
+    public void write(WireBuffer buffer) {
+        apply(buffer, mLengthId, mTextId);
+    }
+
+    @Override
+    public String toString() {
+        return CLASS_NAME + "[" + mLengthId + "] = " + mTextId;
+    }
+
+    public static String name() {
+        return CLASS_NAME;
+    }
+
+    public static int id() {
+        return OP_CODE;
+    }
+
+    /**
+     * Writes out the operation to the buffer
+     *
+     * @param buffer write command to this buffer
+     * @param lengthId the id to output
+     * @param textId the id of the text to measure
+     */
+    public static void apply(WireBuffer buffer, int lengthId, int textId) {
+        buffer.start(OP_CODE);
+        buffer.writeInt(lengthId);
+        buffer.writeInt(textId);
+    }
+
+    public static void read(WireBuffer buffer, List<Operation> operations) {
+        int lengthId = buffer.readInt();
+        int textId = buffer.readInt();
+        operations.add(new TextLength(lengthId, textId));
+    }
+
+    public static void documentation(DocumentationBuilder doc) {
+        doc.operation("Expressions Operations", OP_CODE, CLASS_NAME)
+                .description("get the length of the text and store in float table")
+                .field(INT, "id", "id of float length")
+                .field(INT, "value", "index of text");
+    }
+
+    @Override
+    public void apply(RemoteContext context) {
+        context.loadFloat(mLengthId, context.getText(mTextId).length());
+    }
+
+    @Override
+    public String deepToString(String indent) {
+        return indent + toString();
+    }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/TextLookup.java b/core/java/com/android/internal/widget/remotecompose/core/operations/TextLookup.java
new file mode 100644
index 0000000..b04d698
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/TextLookup.java
@@ -0,0 +1,127 @@
+/*
+ * 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.widget.remotecompose.core.operations;
+
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.FLOAT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
+
+import com.android.internal.widget.remotecompose.core.Operation;
+import com.android.internal.widget.remotecompose.core.Operations;
+import com.android.internal.widget.remotecompose.core.RemoteContext;
+import com.android.internal.widget.remotecompose.core.VariableSupport;
+import com.android.internal.widget.remotecompose.core.WireBuffer;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+
+import java.util.List;
+
+/**
+ * Operation convert floats to text This command is structured
+ * [command][textID][before,after][flags] before and after define number of digits before and after
+ * the decimal point
+ */
+public class TextLookup implements Operation, VariableSupport {
+    private static final int OP_CODE = Operations.TEXT_LOOKUP;
+    private static final String CLASS_NAME = "TextFromFloat";
+    public int mTextId;
+    public int mDataSetId;
+    public float mOutIndex, mIndex;
+
+    public static final int MAX_STRING_SIZE = 4000;
+
+    public TextLookup(int textId, int dataSetId, float index) {
+        this.mTextId = textId;
+        this.mDataSetId = dataSetId;
+        this.mOutIndex = this.mIndex = index;
+    }
+
+    @Override
+    public void write(WireBuffer buffer) {
+        apply(buffer, mTextId, mDataSetId, mIndex);
+    }
+
+    @Override
+    public String toString() {
+        return "TextLookup["
+                + Utils.idString(mTextId)
+                + "] = "
+                + Utils.idString(mDataSetId)
+                + " "
+                + Utils.floatToString(mIndex);
+    }
+
+    @Override
+    public void updateVariables(RemoteContext context) {
+        if (Float.isNaN(mIndex)) {
+            mOutIndex = context.getFloat(Utils.idFromNan(mIndex));
+        }
+    }
+
+    @Override
+    public void registerListening(RemoteContext context) {
+        if (Float.isNaN(mIndex)) {
+            context.listensTo(Utils.idFromNan(mIndex), this);
+        }
+    }
+
+    public static String name() {
+        return CLASS_NAME;
+    }
+
+    public static int id() {
+        return OP_CODE;
+    }
+
+    /**
+     * Writes out the operation to the buffer
+     *
+     * @param buffer buffer to write to
+     * @param textId the id of the output text
+     * @param dataSet float pointer to the array/list to turn int a string
+     * @param index index of element to return
+     */
+    public static void apply(WireBuffer buffer, int textId, int dataSet, float index) {
+        buffer.start(OP_CODE);
+        buffer.writeInt(textId);
+        buffer.writeInt(dataSet);
+        buffer.writeFloat(index);
+    }
+
+    public static void read(WireBuffer buffer, List<Operation> operations) {
+        int textId = buffer.readInt();
+        int dataSetId = buffer.readInt();
+        float index = buffer.readFloat();
+        operations.add(new TextLookup(textId, dataSetId, index));
+    }
+
+    public static void documentation(DocumentationBuilder doc) {
+        doc.operation("Expressions Operations", OP_CODE, CLASS_NAME)
+                .description("Look an array and turn into a text object")
+                .field(INT, "textId", "id of the text generated")
+                .field(FLOAT, "dataSet", "float pointer to the array/list to turn int a string")
+                .field(FLOAT, "index", "index of element to return");
+    }
+
+    @Override
+    public void apply(RemoteContext context) {
+        int id = context.getCollectionsAccess().getId(mDataSetId, (int) mOutIndex);
+        context.loadText(mTextId, context.getText(id));
+    }
+
+    @Override
+    public String deepToString(String indent) {
+        return indent + toString();
+    }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/TextLookupInt.java b/core/java/com/android/internal/widget/remotecompose/core/operations/TextLookupInt.java
new file mode 100644
index 0000000..171bea2
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/TextLookupInt.java
@@ -0,0 +1,120 @@
+/*
+ * 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.widget.remotecompose.core.operations;
+
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
+
+import com.android.internal.widget.remotecompose.core.Operation;
+import com.android.internal.widget.remotecompose.core.Operations;
+import com.android.internal.widget.remotecompose.core.RemoteContext;
+import com.android.internal.widget.remotecompose.core.VariableSupport;
+import com.android.internal.widget.remotecompose.core.WireBuffer;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
+
+import java.util.List;
+
+/** Operation convert int index of a list to text */
+public class TextLookupInt implements Operation, VariableSupport {
+    private static final int OP_CODE = Operations.TEXT_LOOKUP_INT;
+    private static final String CLASS_NAME = "TextFromINT";
+    public int mTextId;
+    public int mDataSetId;
+    public int mOutIndex;
+    public int mIndex;
+
+    public static final int MAX_STRING_SIZE = 4000;
+
+    public TextLookupInt(int textId, int dataSetId, int indexId) {
+        this.mTextId = textId;
+        this.mDataSetId = dataSetId;
+        this.mOutIndex = this.mIndex = indexId;
+    }
+
+    @Override
+    public void write(WireBuffer buffer) {
+        apply(buffer, mTextId, mDataSetId, mIndex);
+    }
+
+    @Override
+    public String toString() {
+        return "TextLookupInt["
+                + Utils.idString(mTextId)
+                + "] = "
+                + Utils.idString(mDataSetId)
+                + " "
+                + mIndex;
+    }
+
+    @Override
+    public void updateVariables(RemoteContext context) {
+        mOutIndex = context.getInteger(mIndex);
+    }
+
+    @Override
+    public void registerListening(RemoteContext context) {
+        context.listensTo(mIndex, this);
+    }
+
+    public static String name() {
+        return CLASS_NAME;
+    }
+
+    public static int id() {
+        return OP_CODE;
+    }
+
+    /**
+     * Writes out the operation to the buffer
+     *
+     * @param buffer buffer to write to
+     * @param textId the id of the output text
+     * @param dataSet float pointer to the array/list to turn int a string
+     * @param indexId index of element to return
+     */
+    public static void apply(WireBuffer buffer, int textId, int dataSet, int indexId) {
+        buffer.start(OP_CODE);
+        buffer.writeInt(textId);
+        buffer.writeInt(dataSet);
+        buffer.writeInt(indexId);
+    }
+
+    public static void read(WireBuffer buffer, List<Operation> operations) {
+        int textId = buffer.readInt();
+        int dataSetId = buffer.readInt();
+        int indexId = buffer.readInt();
+        operations.add(new TextLookupInt(textId, dataSetId, indexId));
+    }
+
+    public static void documentation(DocumentationBuilder doc) {
+        doc.operation("Expressions Operations", OP_CODE, CLASS_NAME)
+                .description("Look up an array and turn into a text object")
+                .field(DocumentedOperation.INT, "textId", "id of the text generated")
+                .field(INT, "dataSetId", "id to the array/list to turn int a string")
+                .field(INT, "index", "index of the element to return");
+    }
+
+    @Override
+    public void apply(RemoteContext context) {
+        int id = context.getCollectionsAccess().getId(mDataSetId, (int) mOutIndex);
+        context.loadText(mTextId, context.getText(id));
+    }
+
+    @Override
+    public String deepToString(String indent) {
+        return indent + toString();
+    }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/TextMeasure.java b/core/java/com/android/internal/widget/remotecompose/core/operations/TextMeasure.java
new file mode 100644
index 0000000..0281d69
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/TextMeasure.java
@@ -0,0 +1,140 @@
+/*
+ * 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.widget.remotecompose.core.operations;
+
+import static com.android.internal.widget.remotecompose.core.PaintContext.TEXT_MEASURE_FONT_HEIGHT;
+import static com.android.internal.widget.remotecompose.core.PaintContext.TEXT_MEASURE_MONOSPACE_WIDTH;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
+
+import com.android.internal.widget.remotecompose.core.Operation;
+import com.android.internal.widget.remotecompose.core.Operations;
+import com.android.internal.widget.remotecompose.core.PaintContext;
+import com.android.internal.widget.remotecompose.core.PaintOperation;
+import com.android.internal.widget.remotecompose.core.WireBuffer;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+
+import java.util.List;
+
+/** Operation to Measure Text data */
+public class TextMeasure extends PaintOperation {
+    private static final int OP_CODE = Operations.TEXT_MEASURE;
+    private static final String CLASS_NAME = "TextMeasure";
+    public int mId;
+    public int mTextId;
+    public int mType;
+
+    public static final int MEASURE_WIDTH = 0;
+    public static final int MEASURE_HEIGHT = 1;
+    public static final int MEASURE_LEFT = 2;
+    public static final int MEASURE_RIGHT = 3;
+    public static final int MEASURE_TOP = 4;
+    public static final int MEASURE_BOTTOM = 5;
+
+    /** a << 8 shifted {@link PaintContext#getTextBounds} */
+    public static final int MEASURE_MONOSPACE_FLAG = TEXT_MEASURE_MONOSPACE_WIDTH << 8;
+
+    public static final int MEASURE_MAX_HEIGHT_FLAG = TEXT_MEASURE_FONT_HEIGHT << 8;
+
+    public TextMeasure(int id, int textId, int type) {
+        this.mId = id;
+        this.mTextId = textId;
+        this.mType = type;
+    }
+
+    @Override
+    public void write(WireBuffer buffer) {
+        apply(buffer, mId, mTextId, mType);
+    }
+
+    @Override
+    public String toString() {
+        return "FloatConstant[" + mId + "] = " + mTextId + " " + mType;
+    }
+
+    public static String name() {
+        return CLASS_NAME;
+    }
+
+    public static int id() {
+        return OP_CODE;
+    }
+
+    /**
+     * Writes out the operation to the buffer
+     *
+     * @param buffer write command to this buffer
+     * @param id the id
+     * @param textId the id
+     * @param type the value of the float
+     */
+    public static void apply(WireBuffer buffer, int id, int textId, int type) {
+        buffer.start(OP_CODE);
+        buffer.writeInt(id);
+        buffer.writeInt(textId);
+        buffer.writeInt(type);
+    }
+
+    public static void read(WireBuffer buffer, List<Operation> operations) {
+        int id = buffer.readInt();
+        int textId = buffer.readInt();
+        int type = buffer.readInt();
+        operations.add(new TextMeasure(id, textId, type));
+    }
+
+    public static void documentation(DocumentationBuilder doc) {
+        doc.operation("Expressions Operations", OP_CODE, CLASS_NAME)
+                .description("A float and its associated id")
+                .field(INT, "id", "id of float result of the measure")
+                .field(INT, "textId", "id of text")
+                .field(INT, "type", "type: measure 0=width,1=height");
+    }
+
+    @Override
+    public String deepToString(String indent) {
+        return indent + toString();
+    }
+
+    float[] mBounds = new float[4];
+
+    @Override
+    public void paint(PaintContext context) {
+        int val = mType & 255;
+        int flags = mType >> 8;
+        context.getTextBounds(mTextId, 0, -1, flags, mBounds);
+        switch (val) {
+            case MEASURE_WIDTH:
+                context.getContext().loadFloat(mId, mBounds[2] - mBounds[0]);
+                break;
+            case MEASURE_HEIGHT:
+                context.getContext().loadFloat(mId, mBounds[3] - mBounds[1]);
+                break;
+            case MEASURE_LEFT:
+                context.getContext().loadFloat(mId, mBounds[0]);
+                break;
+            case MEASURE_TOP:
+                context.getContext().loadFloat(mId, mBounds[1]);
+                break;
+            case MEASURE_RIGHT:
+                context.getContext().loadFloat(mId, mBounds[2]);
+
+                break;
+            case MEASURE_BOTTOM:
+                context.getContext().loadFloat(mId, mBounds[3]);
+
+                break;
+        }
+    }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/TextMerge.java b/core/java/com/android/internal/widget/remotecompose/core/operations/TextMerge.java
index dd78223..78cc674 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/TextMerge.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/TextMerge.java
@@ -15,19 +15,18 @@
  */
 package com.android.internal.widget.remotecompose.core.operations;
 
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
 
 import com.android.internal.widget.remotecompose.core.Operation;
 import com.android.internal.widget.remotecompose.core.Operations;
 import com.android.internal.widget.remotecompose.core.RemoteContext;
 import com.android.internal.widget.remotecompose.core.WireBuffer;
 import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
 
 import java.util.List;
 
-/**
- * Operation to deal with Text data
- */
+/** Operation to deal with Text data */
 public class TextMerge implements Operation {
     private static final int OP_CODE = Operations.TEXT_MERGE;
     private static final String CLASS_NAME = "TextMerge";
@@ -51,7 +50,6 @@
         return "TextMerge[" + mTextId + "] = [" + mSrcId1 + " ] + [ " + mSrcId2 + "]";
     }
 
-
     public static String name() {
         return CLASS_NAME;
     }
@@ -84,16 +82,11 @@
     }
 
     public static void documentation(DocumentationBuilder doc) {
-        doc.operation("Data Operations",
-                        OP_CODE,
-                        CLASS_NAME)
+        doc.operation("Data Operations", OP_CODE, CLASS_NAME)
                 .description("Merge two string into one")
-                .field(INT, "textId",
-                        "id of the text")
-                .field(INT, "srcTextId1",
-                        "id of the path")
-                .field(INT, "srcTextId1",
-                        "x Shift of the text");
+                .field(DocumentedOperation.INT, "textId", "id of the text")
+                .field(INT, "srcTextId1", "id of the path")
+                .field(INT, "srcTextId1", "x Shift of the text");
     }
 
     @Override
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/Theme.java b/core/java/com/android/internal/widget/remotecompose/core/operations/Theme.java
index 52ae7fe..845f25d 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/Theme.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/Theme.java
@@ -15,7 +15,7 @@
  */
 package com.android.internal.widget.remotecompose.core.operations;
 
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
 
 import com.android.internal.widget.remotecompose.core.Operation;
 import com.android.internal.widget.remotecompose.core.Operations;
@@ -27,10 +27,9 @@
 import java.util.List;
 
 /**
- * Set a current theme, applied to the following operations in the document.
- * This can be used to "tag" the subsequent operations to a given theme. On playback,
- * we can then filter operations depending on the chosen theme.
- *
+ * Set a current theme, applied to the following operations in the document. This can be used to
+ * "tag" the subsequent operations to a given theme. On playback, we can then filter operations
+ * depending on the chosen theme.
  */
 public class Theme implements RemoteComposeOperation {
     private static final int OP_CODE = Operations.THEME;
@@ -43,10 +42,7 @@
     /**
      * we can then filter operations depending on the chosen theme.
      *
-     * @param theme the theme we are interested in:
-     *              - Theme.UNSPECIFIED
-     *              - Theme.DARK
-     *              - Theme.LIGHT
+     * @param theme the theme we are interested in: - Theme.UNSPECIFIED - Theme.DARK - Theme.LIGHT
      */
     public Theme(int theme) {
         this.mTheme = theme;
@@ -76,7 +72,6 @@
         return CLASS_NAME;
     }
 
-
     public static int id() {
         return OP_CODE;
     }
@@ -86,7 +81,6 @@
         buffer.writeInt(theme);
     }
 
-
     public static void read(WireBuffer buffer, List<Operation> operations) {
         int theme = buffer.readInt();
         operations.add(new Theme(theme));
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/Utils.java b/core/java/com/android/internal/widget/remotecompose/core/operations/Utils.java
index 6e858c7..8ebb40c 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/Utils.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/Utils.java
@@ -15,9 +15,7 @@
  */
 package com.android.internal.widget.remotecompose.core.operations;
 
-/**
- * Utilities to be used across all core operations
- */
+/** Utilities to be used across all core operations */
 public class Utils {
     public static float asNan(int v) {
         return Float.intBitsToFloat(v | -0x800000);
@@ -28,8 +26,16 @@
         return b & 0x3FFFFF;
     }
 
+    public static long idFromLong(long v) {
+        return v - 0x100000000L;
+    }
+
     public static String idStringFromNan(float value) {
         int b = Float.floatToRawIntBits(value) & 0x3FFFFF;
+        return idString(b);
+    }
+
+    public static String idString(int b) {
         return (b > 0xFFFFF) ? "A_" + (b & 0xFFFFF) : "" + b;
     }
 
@@ -38,8 +44,7 @@
     }
 
     /**
-     * trim a string to n characters if needing to trim
-     * end in "..."
+     * trim a string to n characters if needing to trim end in "..."
      *
      * @param str
      * @param n
@@ -54,6 +59,7 @@
 
     /**
      * print the id and the value of a float
+     *
      * @param idvalue
      * @param value
      * @return
@@ -70,6 +76,7 @@
 
     /**
      * Convert float to string but render nan id in brackets [n]
+     *
      * @param value
      * @return
      */
@@ -85,17 +92,25 @@
 
     /**
      * Debugging util to print a message and include the file/line it came from
+     *
      * @param str
      */
     public static void log(String str) {
         StackTraceElement s = new Throwable().getStackTrace()[1];
-        System.out.println("(" + s.getFileName()
-                + ":" + s.getLineNumber() + "). "
-                + s.getMethodName() + "() " + str);
+        System.out.println(
+                "("
+                        + s.getFileName()
+                        + ":"
+                        + s.getLineNumber()
+                        + "). "
+                        + s.getMethodName()
+                        + "() "
+                        + str);
     }
 
     /**
      * Debugging util to print the stack
+     *
      * @param str
      * @param n
      */
@@ -104,8 +119,8 @@
         for (int i = 1; i < n + 1; i++) {
             StackTraceElement s = st[i];
             String space = new String(new char[i]).replace('\0', ' ');
-            System.out.println(space + "(" + s.getFileName()
-                    + ":" + s.getLineNumber() + ")." + str);
+            System.out.println(
+                    space + "(" + s.getFileName() + ":" + s.getLineNumber() + ")." + str);
         }
     }
 
@@ -136,8 +151,8 @@
     }
 
     /**
-     * Interpolate two colors.
-     * gamma corrected colors are interpolated in the form c1 * (1-t) + c2 * t
+     * Interpolate two colors. gamma corrected colors are interpolated in the form c1 * (1-t) + c2 *
+     * t
      *
      * @param c1
      * @param c2
@@ -183,7 +198,6 @@
         int outb = clamp((int) ((float) Math.pow(f_b, 1.0 / 2.2) * 255.0f));
         int outa = clamp((int) (f_a * 255.0f));
 
-
         return (outa << 24 | outr << 16 | outg << 8 | outb);
     }
 
@@ -205,9 +219,9 @@
     /**
      * convert hue saturation and value to RGB
      *
-     * @param hue        0..1
+     * @param hue 0..1
      * @param saturation 0..1 0=on the gray scale
-     * @param value      0..1 0=black
+     * @param value 0..1 0=black
      * @return
      */
     public static int hsvToRgb(float hue, float saturation, float value) {
@@ -230,7 +244,6 @@
                 return 0XFF000000 | (t << 16) + (p << 8) + v;
             case 5:
                 return 0XFF000000 | (v << 16) + (p << 8) + q;
-
         }
         return 0;
     }
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ActionOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ActionOperation.java
index 7588c794..bdc2a886 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ActionOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ActionOperation.java
@@ -20,11 +20,10 @@
 import com.android.internal.widget.remotecompose.core.RemoteContext;
 import com.android.internal.widget.remotecompose.core.operations.utilities.StringSerializer;
 
-/**
- * Operations representing actions on the document
- */
+/** Operations representing actions on the document */
 public interface ActionOperation extends Operation {
     void serializeToString(int indent, StringSerializer serializer);
-    void runAction(RemoteContext context, CoreDocument document,
-                   Component component, float x, float y);
+
+    void runAction(
+            RemoteContext context, CoreDocument document, Component component, float x, float y);
 }
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/CanvasContent.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/CanvasContent.java
index fd35017..9d80d3c 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/CanvasContent.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/CanvasContent.java
@@ -15,7 +15,7 @@
  */
 package com.android.internal.widget.remotecompose.core.operations.layout;
 
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
 
 import com.android.internal.widget.remotecompose.core.Operation;
 import com.android.internal.widget.remotecompose.core.Operations;
@@ -24,13 +24,17 @@
 
 import java.util.List;
 
-/**
- * Represents the content of a CanvasLayout (i.e. contains the canvas commands)
- */
+/** Represents the content of a CanvasLayout (i.e. contains the canvas commands) */
 public class CanvasContent extends Component implements ComponentStartOperation {
 
-    public CanvasContent(int componentId, float x, float y,
-                         float width, float height, Component parent, int animationId) {
+    public CanvasContent(
+            int componentId,
+            float x,
+            float y,
+            float width,
+            float height,
+            Component parent,
+            int animationId) {
         super(parent, componentId, animationId, x, y, width, height);
     }
 
@@ -42,7 +46,8 @@
         return Operations.LAYOUT_CANVAS_CONTENT;
     }
 
-    @Override protected String getSerializedName() {
+    @Override
+    protected String getSerializedName() {
         return "CANVAS_CONTENT";
     }
 
@@ -53,8 +58,7 @@
 
     public static void read(WireBuffer buffer, List<Operation> operations) {
         int componentId = buffer.readInt();
-        operations.add(new CanvasContent(
-                componentId, 0, 0, 0, 0, null, -1));
+        operations.add(new CanvasContent(componentId, 0, 0, 0, 0, null, -1));
     }
 
     public static void documentation(DocumentationBuilder doc) {
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ClickModifierEnd.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ClickModifierEnd.java
index f7c6ce2..fe726ac 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ClickModifierEnd.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ClickModifierEnd.java
@@ -57,14 +57,14 @@
         buffer.start(id());
     }
 
-
     public static void read(WireBuffer buffer, List<Operation> operations) {
         operations.add(new ClickModifierEnd());
     }
 
     public static void documentation(DocumentationBuilder doc) {
         doc.operation("Layout Operations", id(), name())
-                .description("End tag for click modifiers. This operation marks the end"
-                        + "of a click modifier");
+                .description(
+                        "End tag for click modifiers. This operation marks the end"
+                                + "of a click modifier");
     }
 }
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ClickModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ClickModifierOperation.java
index d75f70b..d5ff07d 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ClickModifierOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ClickModifierOperation.java
@@ -35,14 +35,11 @@
 import java.util.ArrayList;
 import java.util.List;
 
-/**
- * Represents a click modifier + actions
- */
+/** Represents a click modifier + actions */
 public class ClickModifierOperation extends PaintOperation
         implements ModifierOperation, DecoratorComponent {
     private static final int OP_CODE = Operations.MODIFIER_CLICK;
 
-
     long mAnimateRippleStart = 0;
     float mAnimateRippleX = 0f;
     float mAnimateRippleY = 0f;
@@ -60,6 +57,7 @@
         mAnimateRippleX = x;
         mAnimateRippleY = y;
     }
+
     public ArrayList<Operation> mList = new ArrayList<>();
 
     public ArrayList<Operation> getList() {
@@ -107,14 +105,14 @@
         context.savePaint();
         mPaint.reset();
 
-        FloatAnimation anim1 = new FloatAnimation(Easing.CUBIC_STANDARD, 1f,
-                null, Float.NaN, Float.NaN);
+        FloatAnimation anim1 =
+                new FloatAnimation(Easing.CUBIC_STANDARD, 1f, null, Float.NaN, Float.NaN);
         anim1.setInitialValue(0f);
         anim1.setTargetValue(1f);
         float tween = anim1.get(progress);
 
-        FloatAnimation anim2 = new FloatAnimation(Easing.CUBIC_STANDARD, 0.5f,
-                null, Float.NaN, Float.NaN);
+        FloatAnimation anim2 =
+                new FloatAnimation(Easing.CUBIC_STANDARD, 0.5f, null, Float.NaN, Float.NaN);
         anim2.setInitialValue(0f);
         anim2.setTargetValue(1f);
         float tweenRadius = anim2.get(progress);
@@ -149,8 +147,11 @@
     }
 
     @Override
-    public void onClick(RemoteContext context, CoreDocument document,
-                        Component component, float x, float y) {
+    public void onClick(
+            RemoteContext context, CoreDocument document, Component component, float x, float y) {
+        if (!component.isVisible()) {
+            return;
+        }
         locationInWindow[0] = 0f;
         locationInWindow[1] = 0f;
         component.getLocationInWindow(locationInWindow);
@@ -176,7 +177,8 @@
 
     public static void documentation(DocumentationBuilder doc) {
         doc.operation("Layout Operations", OP_CODE, name())
-                .description("Click modifier. This operation contains"
-                        + " a list of action executed on click");
+                .description(
+                        "Click modifier. This operation contains"
+                                + " a list of action executed on click");
     }
 }
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/Component.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/Component.java
index fca0b13..96dffca 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/Component.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/Component.java
@@ -38,9 +38,7 @@
 import java.util.ArrayList;
 import java.util.HashSet;
 
-/**
- * Generic Component class
- */
+/** Generic Component class */
 public class Component extends PaintOperation implements Measurable, SerializableToString {
 
     private static final boolean DEBUG = false;
@@ -64,7 +62,6 @@
     PaintBundle mPaint = new PaintBundle();
     protected HashSet<ComponentValue> mComponentValues = new HashSet<>();
 
-
     public ArrayList<Operation> getList() {
         return mList;
     }
@@ -120,27 +117,27 @@
      */
     private void updateComponentValues(RemoteContext context) {
         if (DEBUG) {
-            System.out.println("UPDATE COMPONENT VALUES ("
-                    + mComponentValues.size()
-                    + ") FOR " + mComponentId);
+            System.out.println(
+                    "UPDATE COMPONENT VALUES ("
+                            + mComponentValues.size()
+                            + ") FOR "
+                            + mComponentId);
         }
         for (ComponentValue v : mComponentValues) {
             switch (v.getType()) {
-                case ComponentValue.WIDTH: {
+                case ComponentValue.WIDTH:
                     context.loadFloat(v.getValueId(), mWidth);
                     if (DEBUG) {
                         System.out.println("Updating WIDTH for " + mComponentId + " to " + mWidth);
                     }
-                }
-                break;
-                case ComponentValue.HEIGHT: {
+                    break;
+                case ComponentValue.HEIGHT:
                     context.loadFloat(v.getValueId(), mHeight);
                     if (DEBUG) {
-                        System.out.println("Updating HEIGHT for " + mComponentId
-                                + " to " + mHeight);
+                        System.out.println(
+                                "Updating HEIGHT for " + mComponentId + " to " + mHeight);
                     }
-                }
-                break;
+                    break;
             }
         }
     }
@@ -153,8 +150,14 @@
         mAnimationId = id;
     }
 
-    public Component(Component parent, int componentId, int animationId,
-                     float x, float y, float width, float height) {
+    public Component(
+            Component parent,
+            int componentId,
+            int animationId,
+            float x,
+            float y,
+            float width,
+            float height) {
         this.mComponentId = componentId;
         this.mX = x;
         this.mY = y;
@@ -164,15 +167,20 @@
         this.mAnimationId = animationId;
     }
 
-    public Component(int componentId, float x, float y, float width, float height,
-                     Component parent) {
+    public Component(
+            int componentId, float x, float y, float width, float height, Component parent) {
         this(parent, componentId, -1, x, y, width, height);
     }
 
     public Component(Component component) {
-        this(component.mParent, component.mComponentId, component.mAnimationId,
-                component.mX, component.mY, component.mWidth, component.mHeight
-        );
+        this(
+                component.mParent,
+                component.mComponentId,
+                component.mAnimationId,
+                component.mX,
+                component.mY,
+                component.mWidth,
+                component.mHeight);
         mList.addAll(component.mList);
         finalizeCreation();
     }
@@ -199,8 +207,8 @@
     }
 
     /**
-     * This traverses the component tree and make sure to
-     * update variables referencing the component dimensions as needed.
+     * This traverses the component tree and make sure to update variables referencing the component
+     * dimensions as needed.
      *
      * @param context the current context
      */
@@ -223,9 +231,9 @@
     }
 
     public enum Visibility {
+        GONE,
         VISIBLE,
-        INVISIBLE,
-        GONE
+        INVISIBLE
     }
 
     public boolean isVisible() {
@@ -239,15 +247,43 @@
     }
 
     public void setVisibility(Visibility visibility) {
-        if (visibility != mVisibility) {
+        if (visibility != mVisibility || visibility != mScheduledVisibility) {
             mScheduledVisibility = visibility;
             invalidateMeasure();
         }
     }
 
     @Override
-    public void measure(PaintContext context, float minWidth, float maxWidth,
-                        float minHeight, float maxHeight, MeasurePass measure) {
+    public boolean suitableForTransition(Operation o) {
+        if (!(o instanceof Component)) {
+            return false;
+        }
+        if (mList.size() != ((Component) o).mList.size()) {
+            return false;
+        }
+        for (int i = 0; i < mList.size(); i++) {
+            Operation o1 = mList.get(i);
+            Operation o2 = ((Component) o).mList.get(i);
+            if (o1 instanceof Component && o2 instanceof Component) {
+                if (!((Component) o1).suitableForTransition(o2)) {
+                    return false;
+                }
+            }
+            if (o1 instanceof PaintOperation && !((PaintOperation) o1).suitableForTransition(o2)) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    @Override
+    public void measure(
+            PaintContext context,
+            float minWidth,
+            float maxWidth,
+            float minHeight,
+            float maxHeight,
+            MeasurePass measure) {
         ComponentMeasure m = measure.get(this);
         m.setW(mWidth);
         m.setH(mHeight);
@@ -256,19 +292,34 @@
     @Override
     public void layout(RemoteContext context, MeasurePass measure) {
         ComponentMeasure m = measure.get(this);
-        if (!mFirstLayout && context.isAnimationEnabled()
+        if (!mFirstLayout
+                && context.isAnimationEnabled()
                 && !(this instanceof LayoutComponentContent)) {
             if (mAnimateMeasure == null) {
-                ComponentMeasure origin = new ComponentMeasure(mComponentId,
-                        mX, mY, mWidth, mHeight, mVisibility);
-                ComponentMeasure target = new ComponentMeasure(mComponentId,
-                        m.getX(), m.getY(), m.getW(), m.getH(), m.getVisibility());
-                mAnimateMeasure = new AnimateMeasure(context.currentTime, this,
-                        origin, target,
-                        mAnimationSpec.getMotionDuration(), mAnimationSpec.getVisibilityDuration(),
-                        mAnimationSpec.getEnterAnimation(), mAnimationSpec.getExitAnimation(),
-                        mAnimationSpec.getMotionEasingType(),
-                        mAnimationSpec.getVisibilityEasingType());
+                ComponentMeasure origin =
+                        new ComponentMeasure(mComponentId, mX, mY, mWidth, mHeight, mVisibility);
+                ComponentMeasure target =
+                        new ComponentMeasure(
+                                mComponentId,
+                                m.getX(),
+                                m.getY(),
+                                m.getW(),
+                                m.getH(),
+                                m.getVisibility());
+                if (!target.same(origin)) {
+                    mAnimateMeasure =
+                            new AnimateMeasure(
+                                    context.currentTime,
+                                    this,
+                                    origin,
+                                    target,
+                                    mAnimationSpec.getMotionDuration(),
+                                    mAnimationSpec.getVisibilityDuration(),
+                                    mAnimationSpec.getEnterAnimation(),
+                                    mAnimationSpec.getExitAnimation(),
+                                    mAnimationSpec.getMotionEasingType(),
+                                    mAnimationSpec.getVisibilityEasingType());
+                }
             } else {
                 mAnimateMeasure.updateTarget(m, context.currentTime);
             }
@@ -323,19 +374,48 @@
 
     @Override
     public String toString() {
-        return "COMPONENT(<" + mComponentId + "> " + getClass().getSimpleName()
-                + ") [" + mX + "," + mY + " - " + mWidth + " x " + mHeight + "] " + textContent()
-                + " Visibility (" + mVisibility + ") ";
+        return "COMPONENT(<"
+                + mComponentId
+                + "> "
+                + getClass().getSimpleName()
+                + ") ["
+                + mX
+                + ","
+                + mY
+                + " - "
+                + mWidth
+                + " x "
+                + mHeight
+                + "] "
+                + textContent()
+                + " Visibility ("
+                + mVisibility
+                + ") ";
     }
 
     protected String getSerializedName() {
         return "COMPONENT";
     }
 
+    @Override
     public void serializeToString(int indent, StringSerializer serializer) {
-        serializer.append(indent, getSerializedName() + " [" + mComponentId
-                        + ":" + mAnimationId + "] = "
-                        + "[" + mX + ", " + mY + ", " + mWidth + ", " + mHeight + "] "
+        serializer.append(
+                indent,
+                getSerializedName()
+                        + " ["
+                        + mComponentId
+                        + ":"
+                        + mAnimationId
+                        + "] = "
+                        + "["
+                        + mX
+                        + ", "
+                        + mY
+                        + ", "
+                        + mWidth
+                        + ", "
+                        + mHeight
+                        + "] "
                         + mVisibility
                 //        + " [" + mNeedsMeasure + ", " + mNeedsRepaint + "]"
         );
@@ -346,9 +426,7 @@
         // nothing
     }
 
-    /**
-     * Returns the top-level RootLayoutComponent
-     */
+    /** Returns the top-level RootLayoutComponent */
     public RootLayoutComponent getRoot() throws Exception {
         if (this instanceof RootLayoutComponent) {
             return (RootLayoutComponent) this;
@@ -378,8 +456,8 @@
     }
 
     /**
-     * Mark itself as needing to be remeasured, and walk back up the tree
-     * to mark each parents as well.
+     * Mark itself as needing to be remeasured, and walk back up the tree to mark each parents as
+     * well.
      */
     public void invalidateMeasure() {
         needsRepaint();
@@ -411,7 +489,7 @@
 
     public String textContent() {
         StringBuilder builder = new StringBuilder();
-        for (Operation op : mList) {
+        for (Operation ignored : mList) {
             String letter = "";
             // if (op instanceof DrawTextRun) {
             //   letter = "[" + ((DrawTextRun) op).text + "]";
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ComponentEnd.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ComponentEnd.java
index 71decd7..c83ee487 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ComponentEnd.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ComponentEnd.java
@@ -61,14 +61,14 @@
         return 1 + 4 + 4 + 4;
     }
 
-
     public static void read(WireBuffer buffer, List<Operation> operations) {
         operations.add(new ComponentEnd());
     }
 
     public static void documentation(DocumentationBuilder doc) {
         doc.operation("Layout Operations", id(), name())
-                .description("End tag for components / layouts. This operation marks the end"
-                        + "of a component");
+                .description(
+                        "End tag for components / layouts. This operation marks the end"
+                                + "of a component");
     }
 }
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ComponentStart.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ComponentStart.java
index 32ef5ce..72cc9b6 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ComponentStart.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ComponentStart.java
@@ -15,8 +15,8 @@
  */
 package com.android.internal.widget.remotecompose.core.operations.layout;
 
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.FLOAT;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.FLOAT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
 
 import com.android.internal.widget.remotecompose.core.Operation;
 import com.android.internal.widget.remotecompose.core.Operations;
@@ -75,8 +75,19 @@
 
     @Override
     public String toString() {
-        return "COMPONENT_START (type " + mType + " " + typeDescription(mType)
-                + ") - (" + mX + ", " + mY + " - " + mWidth + " x " + mHeight + ")";
+        return "COMPONENT_START (type "
+                + mType
+                + " "
+                + typeDescription(mType)
+                + ") - ("
+                + mX
+                + ", "
+                + mY
+                + " - "
+                + mWidth
+                + " x "
+                + mHeight
+                + ")";
     }
 
     @Override
@@ -149,8 +160,8 @@
         return Operations.COMPONENT_START;
     }
 
-    public static void apply(WireBuffer buffer, int type, int componentId,
-                             float width, float height) {
+    public static void apply(
+            WireBuffer buffer, int type, int componentId, float width, float height) {
         buffer.start(Operations.COMPONENT_START);
         buffer.writeInt(type);
         buffer.writeInt(componentId);
@@ -172,8 +183,8 @@
 
     public static void documentation(DocumentationBuilder doc) {
         doc.operation("Layout Operations", id(), name())
-                .description("Basic component encapsulating draw commands."
-                        + "This is not resizable.")
+                .description(
+                        "Basic component encapsulating draw commands." + "This is not resizable.")
                 .field(INT, "TYPE", "Type of components")
                 .field(INT, "COMPONENT_ID", "unique id for this component")
                 .field(FLOAT, "WIDTH", "width of the component")
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ComponentStartOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ComponentStartOperation.java
index 67964ef..abf2356a 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ComponentStartOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ComponentStartOperation.java
@@ -17,5 +17,4 @@
 
 import com.android.internal.widget.remotecompose.core.Operation;
 
-public interface ComponentStartOperation extends Operation {
-}
+public interface ComponentStartOperation extends Operation {}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/DecoratorComponent.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/DecoratorComponent.java
index 71bf839..314650f 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/DecoratorComponent.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/DecoratorComponent.java
@@ -24,6 +24,7 @@
  */
 public interface DecoratorComponent {
     void layout(RemoteContext context, float width, float height);
-    void onClick(RemoteContext context, CoreDocument document,
-                 Component component, float x, float y);
+
+    void onClick(
+            RemoteContext context, CoreDocument document, Component component, float x, float y);
 }
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LayoutComponent.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LayoutComponent.java
index f4c2131..8172502 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LayoutComponent.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LayoutComponent.java
@@ -32,9 +32,7 @@
 
 import java.util.ArrayList;
 
-/**
- * Component with modifiers and children
- */
+/** Component with modifiers and children */
 public class LayoutComponent extends Component {
 
     protected WidthModifierOperation mWidthModifier = null;
@@ -54,8 +52,14 @@
     protected ComponentModifiers mComponentModifiers = new ComponentModifiers();
     protected ArrayList<Component> mChildrenComponents = new ArrayList<>();
 
-    public LayoutComponent(Component parent, int componentId, int animationId,
-                           float x, float y, float width, float height) {
+    public LayoutComponent(
+            Component parent,
+            int componentId,
+            int animationId,
+            float x,
+            float y,
+            float width,
+            float height) {
         super(parent, componentId, animationId, x, y, width, height);
     }
 
@@ -91,7 +95,6 @@
         return mPaddingBottom;
     }
 
-
     public WidthModifierOperation getWidthModifier() {
         return mWidthModifier;
     }
@@ -237,10 +240,7 @@
         context.restore();
     }
 
-
-    /**
-     * Traverse the modifiers to compute indicated dimension
-     */
+    /** Traverse the modifiers to compute indicated dimension */
     public float computeModifierDefinedWidth() {
         float s = 0f;
         float e = 0f;
@@ -283,9 +283,7 @@
         return s + e;
     }
 
-    /**
-     * Traverse the modifiers to compute indicated dimension
-     */
+    /** Traverse the modifiers to compute indicated dimension */
     public float computeModifierDefinedHeight() {
         float t = 0f;
         float b = 0f;
@@ -328,4 +326,7 @@
         return t + b;
     }
 
+    public ArrayList<Component> getChildrenComponents() {
+        return mChildrenComponents;
+    }
 }
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LayoutComponentContent.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LayoutComponentContent.java
index 5b3b54d..66fd053 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LayoutComponentContent.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LayoutComponentContent.java
@@ -15,7 +15,7 @@
  */
 package com.android.internal.widget.remotecompose.core.operations.layout;
 
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
 
 import com.android.internal.widget.remotecompose.core.Operation;
 import com.android.internal.widget.remotecompose.core.Operations;
@@ -24,13 +24,17 @@
 
 import java.util.List;
 
-/**
- * Represents the content of a LayoutComponent (i.e. the children components)
- */
+/** Represents the content of a LayoutComponent (i.e. the children components) */
 public class LayoutComponentContent extends Component implements ComponentStartOperation {
 
-    public LayoutComponentContent(int componentId, float x, float y,
-                                  float width, float height, Component parent, int animationId) {
+    public LayoutComponentContent(
+            int componentId,
+            float x,
+            float y,
+            float width,
+            float height,
+            Component parent,
+            int animationId) {
         super(parent, componentId, animationId, x, y, width, height);
     }
 
@@ -42,7 +46,8 @@
         return Operations.LAYOUT_CONTENT;
     }
 
-    @Override protected String getSerializedName() {
+    @Override
+    protected String getSerializedName() {
         return "CONTENT";
     }
 
@@ -53,16 +58,16 @@
 
     public static void read(WireBuffer buffer, List<Operation> operations) {
         int componentId = buffer.readInt();
-        operations.add(new LayoutComponentContent(
-                componentId, 0, 0, 0, 0, null, -1));
+        operations.add(new LayoutComponentContent(componentId, 0, 0, 0, 0, null, -1));
     }
 
     public static void documentation(DocumentationBuilder doc) {
         doc.operation("Layout Operations", id(), name())
                 .field(INT, "COMPONENT_ID", "unique id for this component")
-                .description("Container for components. BoxLayout, RowLayout and ColumnLayout "
-                        + "expects a LayoutComponentContent as a child, encapsulating the "
-                        + "components that needs to be laid out.");
+                .description(
+                        "Container for components. BoxLayout, RowLayout and ColumnLayout "
+                                + "expects a LayoutComponentContent as a child, encapsulating the "
+                                + "components that needs to be laid out.");
     }
 
     @Override
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LoopEnd.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LoopEnd.java
new file mode 100644
index 0000000..3086d6a
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LoopEnd.java
@@ -0,0 +1,67 @@
+/*
+ * 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.widget.remotecompose.core.operations.layout;
+
+import com.android.internal.widget.remotecompose.core.Operation;
+import com.android.internal.widget.remotecompose.core.Operations;
+import com.android.internal.widget.remotecompose.core.RemoteContext;
+import com.android.internal.widget.remotecompose.core.WireBuffer;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+
+import java.util.List;
+
+public class LoopEnd implements Operation {
+
+    @Override
+    public void write(WireBuffer buffer) {
+        apply(buffer);
+    }
+
+    @Override
+    public String toString() {
+        return "LOOP_END";
+    }
+
+    @Override
+    public void apply(RemoteContext context) {
+        // nothing
+    }
+
+    @Override
+    public String deepToString(String indent) {
+        return (indent != null ? indent : "") + toString();
+    }
+
+    public static String name() {
+        return "LoopEnd";
+    }
+
+    public static int id() {
+        return Operations.LOOP_END;
+    }
+
+    public static void apply(WireBuffer buffer) {
+        buffer.start(id());
+    }
+
+    public static void read(WireBuffer buffer, List<Operation> operations) {
+        operations.add(new LoopEnd());
+    }
+
+    public static void documentation(DocumentationBuilder doc) {
+        doc.operation("Operations", id(), name()).description("End tag for loops");
+    }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LoopOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LoopOperation.java
new file mode 100644
index 0000000..6910008
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LoopOperation.java
@@ -0,0 +1,116 @@
+/*
+ * 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.widget.remotecompose.core.operations.layout;
+
+import com.android.internal.widget.remotecompose.core.Operation;
+import com.android.internal.widget.remotecompose.core.Operations;
+import com.android.internal.widget.remotecompose.core.PaintContext;
+import com.android.internal.widget.remotecompose.core.PaintOperation;
+import com.android.internal.widget.remotecompose.core.VariableSupport;
+import com.android.internal.widget.remotecompose.core.WireBuffer;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/** Represents a loop of operations */
+public class LoopOperation extends PaintOperation {
+    private static final int OP_CODE = Operations.LOOP_START;
+
+    public ArrayList<Operation> mList = new ArrayList<>();
+
+    int mIndexVariableId;
+    float mUntil = 12;
+    float mFrom = 0;
+    float mStep = 1;
+
+    public LoopOperation(int count, int indexId) {
+        mUntil = count;
+        mIndexVariableId = indexId;
+    }
+
+    public LoopOperation(float count, float from, float step, int indexId) {
+        mUntil = count;
+        mFrom = from;
+        mStep = step;
+        mIndexVariableId = indexId;
+    }
+
+    public ArrayList<Operation> getList() {
+        return mList;
+    }
+
+    @Override
+    public void write(WireBuffer buffer) {
+        apply(buffer, mUntil, mFrom, mStep, mIndexVariableId);
+    }
+
+    @Override
+    public String toString() {
+        return "LoopOperation";
+    }
+
+    @Override
+    public String deepToString(String indent) {
+        return (indent != null ? indent : "") + toString();
+    }
+
+    @Override
+    public void paint(PaintContext context) {
+        if (mIndexVariableId == 0) {
+            for (float i = mFrom; i < mUntil; i += mStep) {
+                for (Operation op : mList) {
+                    op.apply(context.getContext());
+                }
+            }
+        } else {
+            for (float i = mFrom; i < mUntil; i += mStep) {
+                context.getContext().loadFloat(mIndexVariableId, i);
+                for (Operation op : mList) {
+                    if (op instanceof VariableSupport) {
+                        ((VariableSupport) op).updateVariables(context.getContext());
+                    }
+                    op.apply(context.getContext());
+                }
+            }
+        }
+    }
+
+    public static String name() {
+        return "Loop";
+    }
+
+    public static void apply(WireBuffer buffer, float count, float from, float step, int indexId) {
+        buffer.start(OP_CODE);
+        buffer.writeFloat(count);
+        buffer.writeFloat(from);
+        buffer.writeFloat(step);
+        buffer.writeInt(indexId);
+    }
+
+    public static void read(WireBuffer buffer, List<Operation> operations) {
+        float count = buffer.readFloat();
+        float from = buffer.readFloat();
+        float step = buffer.readFloat();
+        int indexId = buffer.readInt();
+        operations.add(new LoopOperation(count, from, step, indexId));
+    }
+
+    public static void documentation(DocumentationBuilder doc) {
+        doc.operation("Operations", OP_CODE, name())
+                .description("Loop. This operation execute" + " a list of action in a loop");
+    }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/RootLayoutComponent.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/RootLayoutComponent.java
index bf1a496..680bb0b 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/RootLayoutComponent.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/RootLayoutComponent.java
@@ -15,7 +15,7 @@
  */
 package com.android.internal.widget.remotecompose.core.operations.layout;
 
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
 
 import com.android.internal.widget.remotecompose.core.Operation;
 import com.android.internal.widget.remotecompose.core.Operations;
@@ -32,38 +32,66 @@
 
 import java.util.List;
 
-/**
- * Represents the root layout component. Entry point to the component tree layout/paint.
- */
+/** Represents the root layout component. Entry point to the component tree layout/paint. */
 public class RootLayoutComponent extends Component implements ComponentStartOperation {
     int mCurrentId = -1;
 
-    public RootLayoutComponent(int componentId, float x, float y,
-                               float width, float height, Component parent, int animationId) {
+    public RootLayoutComponent(
+            int componentId,
+            float x,
+            float y,
+            float width,
+            float height,
+            Component parent,
+            int animationId) {
         super(parent, componentId, animationId, x, y, width, height);
     }
 
-    public RootLayoutComponent(int componentId, float x, float y,
-                               float width, float height, Component parent) {
+    public RootLayoutComponent(
+            int componentId, float x, float y, float width, float height, Component parent) {
         super(parent, componentId, -1, x, y, width, height);
     }
 
     @Override
     public String toString() {
-        return "ROOT " + mComponentId + " (" + mX + ", " + mY + " - "
-                + mWidth + " x " + mHeight + ") " + mVisibility;
+        return "ROOT "
+                + mComponentId
+                + " ("
+                + mX
+                + ", "
+                + mY
+                + " - "
+                + mWidth
+                + " x "
+                + mHeight
+                + ") "
+                + mVisibility;
     }
 
     @Override
     public void serializeToString(int indent, StringSerializer serializer) {
-        serializer.append(indent, "ROOT [" + mComponentId + ":" + mAnimationId
-                + "] = [" + mX + ", " + mY + ", " + mWidth + ", " + mHeight + "] " + mVisibility);
+        serializer.append(
+                indent,
+                "ROOT ["
+                        + mComponentId
+                        + ":"
+                        + mAnimationId
+                        + "] = ["
+                        + mX
+                        + ", "
+                        + mY
+                        + ", "
+                        + mWidth
+                        + ", "
+                        + mHeight
+                        + "] "
+                        + mVisibility);
     }
 
     /**
-     * Traverse the hierarchy and assign generated ids to component without ids.
-     * Most components would already have ids assigned during the document creation, but this
-     * allow us to take care of any components added during the inflation.
+     * Traverse the hierarchy and assign generated ids to component without ids. Most components
+     * would already have ids assigned during the document creation, but this allow us to take care
+     * of any components added during the inflation.
      *
      * @param lastId the last known generated id
      */
@@ -84,9 +112,7 @@
         }
     }
 
-    /**
-     * This will measure then layout the tree of components
-     */
+    /** This will measure then layout the tree of components */
     public void layout(RemoteContext context) {
         if (!mNeedsMeasure) {
             return;
@@ -100,8 +126,7 @@
         for (Operation op : mList) {
             if (op instanceof Measurable) {
                 Measurable m = (Measurable) op;
-                m.measure(context.getPaintContext(),
-                        0f, mWidth, 0f, mHeight, measurePass);
+                m.measure(context.getPaintContext(), 0f, mWidth, 0f, mHeight, measurePass);
                 m.layout(context, measurePass);
             }
         }
@@ -161,15 +186,16 @@
 
     public static void read(WireBuffer buffer, List<Operation> operations) {
         int componentId = buffer.readInt();
-        operations.add(new RootLayoutComponent(
-                componentId, 0, 0, 0, 0, null, -1));
+        operations.add(new RootLayoutComponent(componentId, 0, 0, 0, 0, null, -1));
     }
 
     public static void documentation(DocumentationBuilder doc) {
         doc.operation("Layout Operations", id(), name())
                 .field(INT, "COMPONENT_ID", "unique id for this component")
-                .description("Root element for a document. Other components / layout managers "
-                        + "are children in the component tree starting from this Root component.");
+                .description(
+                        "Root element for a document. Other components / layout managers are"
+                                + " children in the component tree starting from"
+                                + "this Root component.");
     }
 
     @Override
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/animation/AnimateMeasure.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/animation/AnimateMeasure.java
index 1ada733..e450585 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/animation/AnimateMeasure.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/animation/AnimateMeasure.java
@@ -28,7 +28,7 @@
 /**
  * Basic interpolation manager between two ComponentMeasures
  *
- * Handles position, size and visibility
+ * <p>Handles position, size and visibility
  */
 public class AnimateMeasure {
     long mStartTime = System.currentTimeMillis();
@@ -44,18 +44,24 @@
 
     float mP = 0f;
     float mVp = 0f;
-    FloatAnimation mMotionEasing = new FloatAnimation(mMotionEasingType,
-            mDuration / 1000f, null, 0f, Float.NaN);
-    FloatAnimation mVisibilityEasing = new FloatAnimation(mVisibilityEasingType,
-            mDurationVisibilityChange / 1000f,
-            null, 0f, Float.NaN);
+    FloatAnimation mMotionEasing =
+            new FloatAnimation(mMotionEasingType, mDuration / 1000f, null, 0f, Float.NaN);
+    FloatAnimation mVisibilityEasing =
+            new FloatAnimation(
+                    mVisibilityEasingType, mDurationVisibilityChange / 1000f, null, 0f, Float.NaN);
     ParticleAnimation mParticleAnimation;
 
-    public AnimateMeasure(long startTime, Component component, ComponentMeasure original,
-                          ComponentMeasure target, int duration, int durationVisibilityChange,
-                          AnimationSpec.ANIMATION enterAnimation,
-                          AnimationSpec.ANIMATION exitAnimation,
-                          int motionEasingType, int visibilityEasingType) {
+    public AnimateMeasure(
+            long startTime,
+            Component component,
+            ComponentMeasure original,
+            ComponentMeasure target,
+            int duration,
+            int durationVisibilityChange,
+            AnimationSpec.ANIMATION enterAnimation,
+            AnimationSpec.ANIMATION exitAnimation,
+            int motionEasingType,
+            int visibilityEasingType) {
         this.mStartTime = startTime;
         this.mComponent = component;
         this.mOriginal = original;
@@ -64,18 +70,28 @@
         this.mDurationVisibilityChange = durationVisibilityChange;
         this.mEnterAnimation = enterAnimation;
         this.mExitAnimation = exitAnimation;
+        this.mMotionEasingType = motionEasingType;
+        this.mVisibilityEasingType = visibilityEasingType;
+
+        float motionDuration = mDuration / 1000f;
+        float visibilityDuration = mDurationVisibilityChange / 1000f;
+
+        mMotionEasing = new FloatAnimation(mMotionEasingType, motionDuration, null, 0f, Float.NaN);
+        mVisibilityEasing =
+                new FloatAnimation(mVisibilityEasingType, visibilityDuration, null, 0f, Float.NaN);
 
         mMotionEasing.setTargetValue(1f);
         mVisibilityEasing.setTargetValue(1f);
+
         component.mVisibility = target.getVisibility();
     }
 
     public void update(long currentTime) {
         long elapsed = currentTime - mStartTime;
-        mP = Math.min(elapsed / (float) mDuration, 1f);
-        //mP = motionEasing.get(mP);
-        mVp = Math.min(elapsed / (float) mDurationVisibilityChange, 1f);
-       // mVp = mVisibilityEasing.get(mVp);
+        float motionProgress = elapsed / (float) mDuration;
+        float visibilityProgress = elapsed / (float) mDurationVisibilityChange;
+        mP = mMotionEasing.get(motionProgress);
+        mVp = mVisibilityEasing.get(visibilityProgress);
     }
 
     public PaintBundle paint = new PaintBundle();
@@ -117,8 +133,11 @@
                         paint.reset();
                         paint.setColor(0f, 0f, 0f, 1f - mVp);
                         context.applyPaint(paint);
-                        context.saveLayer(mComponent.getX(), mComponent.getY(),
-                                mComponent.getWidth(), mComponent.getHeight());
+                        context.saveLayer(
+                                mComponent.getX(),
+                                mComponent.getY(),
+                                mComponent.getWidth(),
+                                mComponent.getHeight());
                         mComponent.paintingComponent(context);
                         context.restore();
                         context.restorePaint();
@@ -127,8 +146,11 @@
                     case SLIDE_LEFT:
                         context.save();
                         context.translate(-mVp * mComponent.getParent().getWidth(), 0f);
-                        context.saveLayer(mComponent.getX(), mComponent.getY(),
-                                mComponent.getWidth(), mComponent.getHeight());
+                        context.saveLayer(
+                                mComponent.getX(),
+                                mComponent.getY(),
+                                mComponent.getWidth(),
+                                mComponent.getHeight());
                         mComponent.paintingComponent(context);
                         context.restore();
                         context.restore();
@@ -140,8 +162,11 @@
                         paint.setColor(0f, 0f, 0f, 1f);
                         context.applyPaint(paint);
                         context.translate(mVp * mComponent.getParent().getWidth(), 0f);
-                        context.saveLayer(mComponent.getX(), mComponent.getY(),
-                                mComponent.getWidth(), mComponent.getHeight());
+                        context.saveLayer(
+                                mComponent.getX(),
+                                mComponent.getY(),
+                                mComponent.getWidth(),
+                                mComponent.getHeight());
                         mComponent.paintingComponent(context);
                         context.restore();
                         context.restorePaint();
@@ -149,20 +174,24 @@
                         break;
                     case SLIDE_TOP:
                         context.save();
-                        context.translate(0f,
-                                -mVp * mComponent.getParent().getHeight());
-                        context.saveLayer(mComponent.getX(), mComponent.getY(),
-                                mComponent.getWidth(), mComponent.getHeight());
+                        context.translate(0f, -mVp * mComponent.getParent().getHeight());
+                        context.saveLayer(
+                                mComponent.getX(),
+                                mComponent.getY(),
+                                mComponent.getWidth(),
+                                mComponent.getHeight());
                         mComponent.paintingComponent(context);
                         context.restore();
                         context.restore();
                         break;
                     case SLIDE_BOTTOM:
                         context.save();
-                        context.translate(0f,
-                                mVp * mComponent.getParent().getHeight());
-                        context.saveLayer(mComponent.getX(), mComponent.getY(),
-                                mComponent.getWidth(), mComponent.getHeight());
+                        context.translate(0f, mVp * mComponent.getParent().getHeight());
+                        context.saveLayer(
+                                mComponent.getX(),
+                                mComponent.getY(),
+                                mComponent.getWidth(),
+                                mComponent.getHeight());
                         mComponent.paintingComponent(context);
                         context.restore();
                         context.restore();
@@ -189,8 +218,11 @@
                         paint.reset();
                         paint.setColor(0f, 0f, 0f, mVp);
                         context.applyPaint(paint);
-                        context.saveLayer(mComponent.getX(), mComponent.getY(),
-                                mComponent.getWidth(), mComponent.getHeight());
+                        context.saveLayer(
+                                mComponent.getX(),
+                                mComponent.getY(),
+                                mComponent.getWidth(),
+                                mComponent.getHeight());
                         mComponent.paintingComponent(context);
                         context.restore();
                         context.restorePaint();
@@ -202,8 +234,11 @@
                         paint.reset();
                         paint.setColor(0f, 0f, 0f, mVp);
                         context.applyPaint(paint);
-                        context.saveLayer(mComponent.getX(), mComponent.getY(),
-                                mComponent.getWidth(), mComponent.getHeight());
+                        context.saveLayer(
+                                mComponent.getX(),
+                                mComponent.getY(),
+                                mComponent.getWidth(),
+                                mComponent.getHeight());
                         mComponent.paintingComponent(context);
                         context.restore();
                         context.restorePaint();
@@ -211,40 +246,48 @@
                         break;
                     case SLIDE_LEFT:
                         context.save();
-                        context.translate(
-                                (1f - mVp) * mComponent.getParent().getWidth(), 0f);
-                        context.saveLayer(mComponent.getX(), mComponent.getY(),
-                                mComponent.getWidth(), mComponent.getHeight());
+                        context.translate((1f - mVp) * mComponent.getParent().getWidth(), 0f);
+                        context.saveLayer(
+                                mComponent.getX(),
+                                mComponent.getY(),
+                                mComponent.getWidth(),
+                                mComponent.getHeight());
                         mComponent.paintingComponent(context);
                         context.restore();
                         context.restore();
                         break;
                     case SLIDE_RIGHT:
                         context.save();
-                        context.translate(
-                                -(1f - mVp) * mComponent.getParent().getWidth(), 0f);
-                        context.saveLayer(mComponent.getX(), mComponent.getY(),
-                                mComponent.getWidth(), mComponent.getHeight());
+                        context.translate(-(1f - mVp) * mComponent.getParent().getWidth(), 0f);
+                        context.saveLayer(
+                                mComponent.getX(),
+                                mComponent.getY(),
+                                mComponent.getWidth(),
+                                mComponent.getHeight());
                         mComponent.paintingComponent(context);
                         context.restore();
                         context.restore();
                         break;
                     case SLIDE_TOP:
                         context.save();
-                        context.translate(0f,
-                                (1f - mVp) * mComponent.getParent().getHeight());
-                        context.saveLayer(mComponent.getX(), mComponent.getY(),
-                                mComponent.getWidth(), mComponent.getHeight());
+                        context.translate(0f, (1f - mVp) * mComponent.getParent().getHeight());
+                        context.saveLayer(
+                                mComponent.getX(),
+                                mComponent.getY(),
+                                mComponent.getWidth(),
+                                mComponent.getHeight());
                         mComponent.paintingComponent(context);
                         context.restore();
                         context.restore();
                         break;
                     case SLIDE_BOTTOM:
                         context.save();
-                        context.translate(0f,
-                                -(1f - mVp) * mComponent.getParent().getHeight());
-                        context.saveLayer(mComponent.getX(), mComponent.getY(),
-                                mComponent.getWidth(), mComponent.getHeight());
+                        context.translate(0f, -(1f - mVp) * mComponent.getParent().getHeight());
+                        context.saveLayer(
+                                mComponent.getX(),
+                                mComponent.getY(),
+                                mComponent.getWidth(),
+                                mComponent.getHeight());
                         mComponent.paintingComponent(context);
                         context.restore();
                         context.restore();
@@ -300,11 +343,22 @@
         mOriginal.setY(getY());
         mOriginal.setW(getWidth());
         mOriginal.setH(getHeight());
-        mTarget.setX(measure.getX());
-        mTarget.setY(measure.getY());
-        mTarget.setW(measure.getW());
-        mTarget.setH(measure.getH());
-        mTarget.setVisibility(measure.getVisibility());
-        mStartTime = currentTime;
+        float targetX = mTarget.getX();
+        float targetY = mTarget.getY();
+        float targetW = mTarget.getW();
+        float targetH = mTarget.getH();
+        Component.Visibility targetVisibility = mTarget.getVisibility();
+        if (targetX != measure.getX()
+                || targetY != measure.getY()
+                || targetW != measure.getW()
+                || targetH != measure.getH()
+                || targetVisibility != measure.getVisibility()) {
+            mTarget.setX(measure.getX());
+            mTarget.setY(measure.getY());
+            mTarget.setW(measure.getW());
+            mTarget.setH(measure.getH());
+            mTarget.setVisibility(measure.getVisibility());
+            mStartTime = currentTime;
+        }
     }
 }
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/animation/AnimationSpec.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/animation/AnimationSpec.java
index 0f7db36..35533cb 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/animation/AnimationSpec.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/animation/AnimationSpec.java
@@ -15,7 +15,7 @@
  */
 package com.android.internal.widget.remotecompose.core.operations.layout.animation;
 
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
 
 import com.android.internal.widget.remotecompose.core.Operation;
 import com.android.internal.widget.remotecompose.core.Operations;
@@ -26,9 +26,7 @@
 
 import java.util.List;
 
-/**
- * Basic component animation spec
- */
+/** Basic component animation spec */
 public class AnimationSpec implements Operation {
     int mAnimationId = -1;
     int mMotionDuration = 300;
@@ -38,9 +36,14 @@
     ANIMATION mEnterAnimation = ANIMATION.FADE_IN;
     ANIMATION mExitAnimation = ANIMATION.FADE_OUT;
 
-    public AnimationSpec(int animationId, int motionDuration, int motionEasingType,
-                         int visibilityDuration, int visibilityEasingType,
-                         ANIMATION enterAnimation, ANIMATION exitAnimation) {
+    public AnimationSpec(
+            int animationId,
+            int motionDuration,
+            int motionEasingType,
+            int visibilityDuration,
+            int visibilityEasingType,
+            ANIMATION enterAnimation,
+            ANIMATION exitAnimation) {
         this.mAnimationId = animationId;
         this.mMotionDuration = motionDuration;
         this.mMotionEasingType = motionEasingType;
@@ -51,9 +54,14 @@
     }
 
     public AnimationSpec() {
-        this(-1, 300, GeneralEasing.CUBIC_STANDARD,
-                300, GeneralEasing.CUBIC_STANDARD,
-                ANIMATION.FADE_IN, ANIMATION.FADE_OUT);
+        this(
+                -1,
+                600,
+                GeneralEasing.CUBIC_STANDARD,
+                500,
+                GeneralEasing.CUBIC_STANDARD,
+                ANIMATION.FADE_IN,
+                ANIMATION.FADE_OUT);
     }
 
     public int getAnimationId() {
@@ -102,8 +110,15 @@
 
     @Override
     public void write(WireBuffer buffer) {
-        apply(buffer, mAnimationId, mMotionDuration, mMotionEasingType,
-                mVisibilityDuration, mVisibilityEasingType, mEnterAnimation, mExitAnimation);
+        apply(
+                buffer,
+                mAnimationId,
+                mMotionDuration,
+                mMotionEasingType,
+                mVisibilityDuration,
+                mVisibilityEasingType,
+                mEnterAnimation,
+                mExitAnimation);
     }
 
     @Override
@@ -151,10 +166,15 @@
         }
     }
 
-    public static void apply(WireBuffer buffer, int animationId, int motionDuration,
-                             int motionEasingType, int visibilityDuration,
-                             int visibilityEasingType, ANIMATION enterAnimation,
-                             ANIMATION exitAnimation) {
+    public static void apply(
+            WireBuffer buffer,
+            int animationId,
+            int motionDuration,
+            int motionEasingType,
+            int visibilityDuration,
+            int visibilityEasingType,
+            ANIMATION enterAnimation,
+            ANIMATION exitAnimation) {
         buffer.start(Operations.ANIMATION_SPEC);
         buffer.writeInt(animationId);
         buffer.writeInt(motionDuration);
@@ -173,20 +193,25 @@
         int visibilityEasingType = buffer.readInt();
         ANIMATION enterAnimation = intToAnimation(buffer.readInt());
         ANIMATION exitAnimation = intToAnimation(buffer.readInt());
-        AnimationSpec op = new AnimationSpec(animationId, motionDuration, motionEasingType,
-                visibilityDuration, visibilityEasingType, enterAnimation, exitAnimation);
+        AnimationSpec op =
+                new AnimationSpec(
+                        animationId,
+                        motionDuration,
+                        motionEasingType,
+                        visibilityDuration,
+                        visibilityEasingType,
+                        enterAnimation,
+                        exitAnimation);
         operations.add(op);
     }
+
     public static void documentation(DocumentationBuilder doc) {
-        doc.operation("Layout Operations",
-                        id(),
-                        name())
+        doc.operation("Layout Operations", id(), name())
                 .description("define the animation")
                 .field(INT, "animationId", "")
                 .field(INT, "motionDuration", "")
                 .field(INT, "motionEasingType", "")
                 .field(INT, "visibilityDuration", "")
                 .field(INT, "visibilityEasingType", "");
-
     }
 }
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/animation/ParticleAnimation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/animation/ParticleAnimation.java
index 5c5d056..686643f 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/animation/ParticleAnimation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/animation/ParticleAnimation.java
@@ -27,9 +27,13 @@
     HashMap<Integer, ArrayList<Particle>> mAllParticles = new HashMap<>();
 
     PaintBundle mPaint = new PaintBundle();
-    public void animate(PaintContext context, Component component,
-                        ComponentMeasure start, ComponentMeasure end,
-                        float progress) {
+
+    public void animate(
+            PaintContext context,
+            Component component,
+            ComponentMeasure start,
+            ComponentMeasure end,
+            float progress) {
         ArrayList<Particle> particles = mAllParticles.get(component.getComponentId());
         if (particles == null) {
             particles = new ArrayList<Particle>();
@@ -37,9 +41,9 @@
                 float x = (float) Math.random();
                 float y = (float) Math.random();
                 float radius = (float) Math.random();
-                float r = 250f;
-                float g = 250f;
-                float b = 250f;
+                float r = 220f;
+                float g = 220f;
+                float b = 220f;
                 particles.add(new Particle(x, y, radius, r, g, b));
             }
             mAllParticles.put(component.getComponentId(), particles);
@@ -49,12 +53,17 @@
         for (int i = 0; i < particles.size(); i++) {
             Particle particle = particles.get(i);
             mPaint.reset();
-            mPaint.setColor(particle.r, particle.g, particle.b,
-                    200 * (1 - progress));
+            mPaint.setColor(
+                    particle.r / 255f,
+                    particle.g / 255f,
+                    particle.b / 255f,
+                    (200 * (1 - progress)) / 255f);
             context.applyPaint(mPaint);
             float dx = start.getX() + component.getWidth() * particle.x;
-            float dy = start.getY() + component.getHeight() * particle.y
-                    + progress * 0.01f * component.getHeight();
+            float dy =
+                    start.getY()
+                            + component.getHeight() * particle.y
+                            + progress * 0.01f * component.getHeight();
             float dr = (component.getHeight() + 60) * 0.15f * particle.radius + (30 * progress);
             context.drawCircle(dx, dy, dr);
         }
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/BoxLayout.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/BoxLayout.java
index 88a49a66..047a968 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/BoxLayout.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/BoxLayout.java
@@ -15,7 +15,7 @@
  */
 package com.android.internal.widget.remotecompose.core.operations.layout.managers;
 
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
 
 import com.android.internal.widget.remotecompose.core.Operation;
 import com.android.internal.widget.remotecompose.core.Operations;
@@ -30,9 +30,7 @@
 
 import java.util.List;
 
-/**
- * Simple Box layout implementation
- */
+/** Simple Box layout implementation */
 public class BoxLayout extends LayoutManager implements ComponentStartOperation {
 
     public static final int START = 1;
@@ -41,37 +39,68 @@
     public static final int TOP = 4;
     public static final int BOTTOM = 5;
 
-
     int mHorizontalPositioning;
     int mVerticalPositioning;
 
-    public BoxLayout(Component parent, int componentId, int animationId,
-                     float x, float y, float width, float height,
-                     int horizontalPositioning, int verticalPositioning) {
+    public BoxLayout(
+            Component parent,
+            int componentId,
+            int animationId,
+            float x,
+            float y,
+            float width,
+            float height,
+            int horizontalPositioning,
+            int verticalPositioning) {
         super(parent, componentId, animationId, x, y, width, height);
         mHorizontalPositioning = horizontalPositioning;
         mVerticalPositioning = verticalPositioning;
     }
 
-    public BoxLayout(Component parent, int componentId, int animationId,
-                     int horizontalPositioning, int verticalPositioning) {
-        this(parent, componentId, animationId, 0, 0, 0, 0,
-                horizontalPositioning, verticalPositioning);
+    public BoxLayout(
+            Component parent,
+            int componentId,
+            int animationId,
+            int horizontalPositioning,
+            int verticalPositioning) {
+        this(
+                parent,
+                componentId,
+                animationId,
+                0,
+                0,
+                0,
+                0,
+                horizontalPositioning,
+                verticalPositioning);
     }
 
     @Override
     public String toString() {
-        return "BOX [" + mComponentId + ":" + mAnimationId + "] (" + mX + ", "
-                + mY + " - " + mWidth + " x " + mHeight + ") " + mVisibility;
+        return "BOX ["
+                + mComponentId
+                + ":"
+                + mAnimationId
+                + "] ("
+                + mX
+                + ", "
+                + mY
+                + " - "
+                + mWidth
+                + " x "
+                + mHeight
+                + ") "
+                + mVisibility;
     }
 
+    @Override
     protected String getSerializedName() {
         return "BOX";
     }
 
     @Override
-    public void computeWrapSize(PaintContext context, float maxWidth, float maxHeight,
-                                MeasurePass measure, Size size) {
+    public void computeWrapSize(
+            PaintContext context, float maxWidth, float maxHeight, MeasurePass measure, Size size) {
         for (Component c : mChildrenComponents) {
             c.measure(context, 0f, maxWidth, 0f, maxHeight, measure);
             ComponentMeasure m = measure.get(c);
@@ -84,16 +113,20 @@
     }
 
     @Override
-    public void computeSize(PaintContext context, float minWidth, float maxWidth,
-                            float minHeight, float maxHeight, MeasurePass measure) {
+    public void computeSize(
+            PaintContext context,
+            float minWidth,
+            float maxWidth,
+            float minHeight,
+            float maxHeight,
+            MeasurePass measure) {
         for (Component child : mChildrenComponents) {
             child.measure(context, minWidth, maxWidth, minHeight, maxHeight, measure);
         }
     }
 
     @Override
-    public void internalLayoutMeasure(PaintContext context,
-                                      MeasurePass measure) {
+    public void internalLayoutMeasure(PaintContext context, MeasurePass measure) {
         ComponentMeasure selfMeasure = measure.get(this);
         float selfWidth = selfMeasure.getW() - mPaddingLeft - mPaddingRight;
         float selfHeight = selfMeasure.getH() - mPaddingTop - mPaddingBottom;
@@ -136,8 +169,12 @@
         return Operations.LAYOUT_BOX;
     }
 
-    public static void apply(WireBuffer buffer, int componentId, int animationId,
-                             int horizontalPositioning, int verticalPositioning) {
+    public static void apply(
+            WireBuffer buffer,
+            int componentId,
+            int animationId,
+            int horizontalPositioning,
+            int verticalPositioning) {
         buffer.start(Operations.LAYOUT_BOX);
         buffer.writeInt(componentId);
         buffer.writeInt(animationId);
@@ -150,24 +187,32 @@
         int animationId = buffer.readInt();
         int horizontalPositioning = buffer.readInt();
         int verticalPositioning = buffer.readInt();
-        operations.add(new BoxLayout(null, componentId, animationId,
-                horizontalPositioning, verticalPositioning));
+        operations.add(
+                new BoxLayout(
+                        null,
+                        componentId,
+                        animationId,
+                        horizontalPositioning,
+                        verticalPositioning));
     }
 
     public static void documentation(DocumentationBuilder doc) {
         doc.operation("Layout Operations", id(), name())
-                .description("Box layout implementation.\n\n"
-                        + "Child components are laid out independently from one another,\n"
-                        + " and painted in their hierarchy order (first children drawn"
-                        + "before the latter). Horizontal and Vertical positioning"
-                        + "are supported.")
+                .description(
+                        "Box layout implementation.\n\n"
+                                + "Child components are laid out independently from one another,\n"
+                                + " and painted in their hierarchy order (first children drawn"
+                                + "before the latter). Horizontal and Vertical positioning"
+                                + "are supported.")
                 .examplesDimension(150, 100)
                 .exampleImage("Top", "layout-BoxLayout-start-top.png")
                 .exampleImage("Center", "layout-BoxLayout-center-center.png")
                 .exampleImage("Bottom", "layout-BoxLayout-end-bottom.png")
                 .field(INT, "COMPONENT_ID", "unique id for this component")
-                .field(INT, "ANIMATION_ID", "id used to match components,"
-                        + " for animation purposes")
+                .field(
+                        INT,
+                        "ANIMATION_ID",
+                        "id used to match components," + " for animation purposes")
                 .field(INT, "HORIZONTAL_POSITIONING", "horizontal positioning value")
                 .possibleValues("START", BoxLayout.START)
                 .possibleValues("CENTER", BoxLayout.CENTER)
@@ -178,10 +223,8 @@
                 .possibleValues("BOTTOM", BoxLayout.BOTTOM);
     }
 
-
     @Override
     public void write(WireBuffer buffer) {
-        apply(buffer, mComponentId, mAnimationId,
-                mHorizontalPositioning, mVerticalPositioning);
+        apply(buffer, mComponentId, mAnimationId, mHorizontalPositioning, mVerticalPositioning);
     }
 }
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/CanvasLayout.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/CanvasLayout.java
index bce7a77a..f799767 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/CanvasLayout.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/CanvasLayout.java
@@ -15,7 +15,7 @@
  */
 package com.android.internal.widget.remotecompose.core.operations.layout.managers;
 
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
 
 import com.android.internal.widget.remotecompose.core.Operation;
 import com.android.internal.widget.remotecompose.core.Operations;
@@ -29,8 +29,14 @@
 import java.util.List;
 
 public class CanvasLayout extends BoxLayout {
-    public CanvasLayout(Component parent, int componentId, int animationId,
-                        float x, float y, float width, float height) {
+    public CanvasLayout(
+            Component parent,
+            int componentId,
+            int animationId,
+            float x,
+            float y,
+            float width,
+            float height) {
         super(parent, componentId, animationId, x, y, width, height, 0, 0);
     }
 
@@ -40,10 +46,23 @@
 
     @Override
     public String toString() {
-        return "CANVAS [" + mComponentId + ":" + mAnimationId + "] (" + mX + ", "
-                + mY + " - " + mWidth + " x " + mHeight + ") " + mVisibility;
+        return "CANVAS ["
+                + mComponentId
+                + ":"
+                + mAnimationId
+                + "] ("
+                + mX
+                + ", "
+                + mY
+                + " - "
+                + mWidth
+                + " x "
+                + mHeight
+                + ") "
+                + mVisibility;
     }
 
+    @Override
     protected String getSerializedName() {
         return "CANVAS";
     }
@@ -72,13 +91,14 @@
         doc.operation("Layout Operations", id(), name())
                 .description("Canvas implementation. Encapsulate draw operations.\n\n")
                 .field(INT, "COMPONENT_ID", "unique id for this component")
-                .field(INT, "ANIMATION_ID", "id used to match components,"
-                        + " for animation purposes");
+                .field(
+                        INT,
+                        "ANIMATION_ID",
+                        "id used to match components," + " for animation purposes");
     }
 
     @Override
-    public void internalLayoutMeasure(PaintContext context,
-                                      MeasurePass measure) {
+    public void internalLayoutMeasure(PaintContext context, MeasurePass measure) {
         ComponentMeasure selfMeasure = measure.get(this);
         float selfWidth = selfMeasure.getW() - mPaddingLeft - mPaddingRight;
         float selfHeight = selfMeasure.getH() - mPaddingTop - mPaddingBottom;
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/ColumnLayout.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/ColumnLayout.java
index 48d966e..402b784 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/ColumnLayout.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/ColumnLayout.java
@@ -15,8 +15,8 @@
  */
 package com.android.internal.widget.remotecompose.core.operations.layout.managers;
 
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.FLOAT;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.FLOAT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
 
 import com.android.internal.widget.remotecompose.core.Operation;
 import com.android.internal.widget.remotecompose.core.Operations;
@@ -34,8 +34,7 @@
 import java.util.List;
 
 /**
- * Simple Column layout implementation
- * - also supports weight and horizontal/vertical positioning
+ * Simple Column layout implementation - also supports weight and horizontal/vertical positioning
  */
 public class ColumnLayout extends LayoutManager implements ComponentStartOperation {
     public static final int START = 1;
@@ -51,69 +50,122 @@
     int mVerticalPositioning;
     float mSpacedBy = 0f;
 
-    public ColumnLayout(Component parent, int componentId, int animationId,
-                        float x, float y, float width, float height,
-                        int horizontalPositioning, int verticalPositioning, float spacedBy) {
+    public ColumnLayout(
+            Component parent,
+            int componentId,
+            int animationId,
+            float x,
+            float y,
+            float width,
+            float height,
+            int horizontalPositioning,
+            int verticalPositioning,
+            float spacedBy) {
         super(parent, componentId, animationId, x, y, width, height);
         mHorizontalPositioning = horizontalPositioning;
         mVerticalPositioning = verticalPositioning;
         mSpacedBy = spacedBy;
     }
 
-    public ColumnLayout(Component parent, int componentId, int animationId,
-                        int horizontalPositioning, int verticalPositioning, float spacedBy) {
-        this(parent, componentId, animationId, 0, 0, 0, 0,
-                horizontalPositioning, verticalPositioning, spacedBy);
+    public ColumnLayout(
+            Component parent,
+            int componentId,
+            int animationId,
+            int horizontalPositioning,
+            int verticalPositioning,
+            float spacedBy) {
+        this(
+                parent,
+                componentId,
+                animationId,
+                0,
+                0,
+                0,
+                0,
+                horizontalPositioning,
+                verticalPositioning,
+                spacedBy);
     }
 
     @Override
     public String toString() {
-        return "COLUMN [" + mComponentId + ":" + mAnimationId + "] (" + mX + ", "
-                + mY + " - " + mWidth + " x " + mHeight + ") " + mVisibility;
+        return "COLUMN ["
+                + mComponentId
+                + ":"
+                + mAnimationId
+                + "] ("
+                + mX
+                + ", "
+                + mY
+                + " - "
+                + mWidth
+                + " x "
+                + mHeight
+                + ") "
+                + mVisibility;
     }
 
+    @Override
     protected String getSerializedName() {
         return "COLUMN";
     }
 
     @Override
-    public void computeWrapSize(PaintContext context, float maxWidth, float maxHeight,
-                                MeasurePass measure, Size size) {
+    public void computeWrapSize(
+            PaintContext context, float maxWidth, float maxHeight, MeasurePass measure, Size size) {
         DebugLog.s(() -> "COMPUTE WRAP SIZE in " + this + " (" + mComponentId + ")");
+        int visibleChildrens = 0;
         for (Component c : mChildrenComponents) {
-            c.measure(context, 0f, maxWidth,
-                    0f, maxHeight, measure);
+            c.measure(context, 0f, maxWidth, 0f, maxHeight, measure);
             ComponentMeasure m = measure.get(c);
-            size.setWidth(Math.max(size.getWidth(), m.getW()));
-            size.setHeight(size.getHeight() + m.getH());
+            if (m.getVisibility() != Visibility.GONE) {
+                size.setWidth(Math.max(size.getWidth(), m.getW()));
+                size.setHeight(size.getHeight() + m.getH());
+                visibleChildrens++;
+            }
         }
         if (!mChildrenComponents.isEmpty()) {
-            size.setHeight(size.getHeight()
-                    + (mSpacedBy * (mChildrenComponents.size() - 1)));
+            size.setHeight(size.getHeight() + (mSpacedBy * (visibleChildrens - 1)));
         }
         DebugLog.e();
     }
 
     @Override
-    public void computeSize(PaintContext context, float minWidth, float maxWidth,
-                            float minHeight, float maxHeight, MeasurePass measure) {
+    public void computeSize(
+            PaintContext context,
+            float minWidth,
+            float maxWidth,
+            float minHeight,
+            float maxHeight,
+            MeasurePass measure) {
         DebugLog.s(() -> "COMPUTE SIZE in " + this + " (" + mComponentId + ")");
         float mh = maxHeight;
         for (Component child : mChildrenComponents) {
             child.measure(context, minWidth, maxWidth, minHeight, mh, measure);
             ComponentMeasure m = measure.get(child);
-            mh -= m.getH();
+            if (m.getVisibility() != Visibility.GONE) {
+                mh -= m.getH();
+            }
         }
         DebugLog.e();
     }
 
     @Override
-    public void internalLayoutMeasure(PaintContext context,
-                                      MeasurePass measure) {
+    public void internalLayoutMeasure(PaintContext context, MeasurePass measure) {
         ComponentMeasure selfMeasure = measure.get(this);
-        DebugLog.s(() -> "INTERNAL LAYOUT " + this + " (" + mComponentId + ") children: "
-                + mChildrenComponents.size() + " size (" + selfMeasure.getW()
-                + " x " + selfMeasure.getH() + ")");
+        DebugLog.s(
+                () ->
+                        "INTERNAL LAYOUT "
+                                + this
+                                + " ("
+                                + mComponentId
+                                + ") children: "
+                                + mChildrenComponents.size()
+                                + " size ("
+                                + selfMeasure.getW()
+                                + " x "
+                                + selfMeasure.getH()
+                                + ")");
         if (mChildrenComponents.isEmpty()) {
             DebugLog.e();
             return;
@@ -127,6 +179,9 @@
         float totalWeights = 0f;
         for (Component child : mChildrenComponents) {
             ComponentMeasure childMeasure = measure.get(child);
+            if (childMeasure.getVisibility() == Visibility.GONE) {
+                continue;
+            }
             if (child instanceof LayoutComponent
                     && ((LayoutComponent) child).getHeightModifier().hasWeight()) {
                 hasWeights = true;
@@ -141,21 +196,34 @@
                 if (child instanceof LayoutComponent
                         && ((LayoutComponent) child).getHeightModifier().hasWeight()) {
                     ComponentMeasure childMeasure = measure.get(child);
+                    if (childMeasure.getVisibility() == Visibility.GONE) {
+                        continue;
+                    }
                     float weight = ((LayoutComponent) child).getHeightModifier().getValue();
                     childMeasure.setH((weight * availableSpace) / totalWeights);
-                    child.measure(context, childMeasure.getW(),
-                            childMeasure.getW(), childMeasure.getH(), childMeasure.getH(), measure);
+                    child.measure(
+                            context,
+                            childMeasure.getW(),
+                            childMeasure.getW(),
+                            childMeasure.getH(),
+                            childMeasure.getH(),
+                            measure);
                 }
             }
         }
 
         childrenHeight = 0f;
+        int visibleChildrens = 0;
         for (Component child : mChildrenComponents) {
             ComponentMeasure childMeasure = measure.get(child);
+            if (childMeasure.getVisibility() == Visibility.GONE) {
+                continue;
+            }
             childrenWidth = Math.max(childrenWidth, childMeasure.getW());
             childrenHeight += childMeasure.getH();
+            visibleChildrens++;
         }
-        childrenHeight += mSpacedBy * (mChildrenComponents.size() - 1);
+        childrenHeight += mSpacedBy * (visibleChildrens - 1);
 
         float tx = 0f;
         float ty = 0f;
@@ -175,24 +243,33 @@
             case SPACE_BETWEEN:
                 for (Component child : mChildrenComponents) {
                     ComponentMeasure childMeasure = measure.get(child);
+                    if (childMeasure.getVisibility() == Visibility.GONE) {
+                        continue;
+                    }
                     total += childMeasure.getH();
                 }
-                verticalGap = (selfHeight - total) / (mChildrenComponents.size() - 1);
+                verticalGap = (selfHeight - total) / (visibleChildrens - 1);
                 break;
             case SPACE_EVENLY:
                 for (Component child : mChildrenComponents) {
                     ComponentMeasure childMeasure = measure.get(child);
+                    if (childMeasure.getVisibility() == Visibility.GONE) {
+                        continue;
+                    }
                     total += childMeasure.getH();
                 }
-                verticalGap = (selfHeight - total) / (mChildrenComponents.size() + 1);
+                verticalGap = (selfHeight - total) / (visibleChildrens + 1);
                 ty = verticalGap;
                 break;
             case SPACE_AROUND:
                 for (Component child : mChildrenComponents) {
                     ComponentMeasure childMeasure = measure.get(child);
+                    if (childMeasure.getVisibility() == Visibility.GONE) {
+                        continue;
+                    }
                     total += childMeasure.getH();
                 }
-                verticalGap = (selfHeight - total) / (mChildrenComponents.size());
+                verticalGap = (selfHeight - total) / visibleChildrens;
                 ty = verticalGap / 2f;
                 break;
         }
@@ -211,6 +288,9 @@
             }
             childMeasure.setX(tx);
             childMeasure.setY(ty);
+            if (childMeasure.getVisibility() == Visibility.GONE) {
+                continue;
+            }
             ty += childMeasure.getH();
             if (mVerticalPositioning == SPACE_BETWEEN
                     || mVerticalPositioning == SPACE_AROUND
@@ -230,8 +310,13 @@
         return Operations.LAYOUT_COLUMN;
     }
 
-    public static void apply(WireBuffer buffer, int componentId, int animationId,
-                             int horizontalPositioning, int verticalPositioning, float spacedBy) {
+    public static void apply(
+            WireBuffer buffer,
+            int componentId,
+            int animationId,
+            int horizontalPositioning,
+            int verticalPositioning,
+            float spacedBy) {
         buffer.start(Operations.LAYOUT_COLUMN);
         buffer.writeInt(componentId);
         buffer.writeInt(animationId);
@@ -246,15 +331,22 @@
         int horizontalPositioning = buffer.readInt();
         int verticalPositioning = buffer.readInt();
         float spacedBy = buffer.readFloat();
-        operations.add(new ColumnLayout(null, componentId, animationId,
-                horizontalPositioning, verticalPositioning, spacedBy));
+        operations.add(
+                new ColumnLayout(
+                        null,
+                        componentId,
+                        animationId,
+                        horizontalPositioning,
+                        verticalPositioning,
+                        spacedBy));
     }
 
     public static void documentation(DocumentationBuilder doc) {
         doc.operation("Layout Operations", id(), name())
-                .description("Column layout implementation, positioning components one"
-                        + " after the other vertically.\n\n"
-                        + "It supports weight and horizontal/vertical positioning.")
+                .description(
+                        "Column layout implementation, positioning components one"
+                                + " after the other vertically.\n\n"
+                                + "It supports weight and horizontal/vertical positioning.")
                 .examplesDimension(100, 400)
                 .exampleImage("Top", "layout-ColumnLayout-start-top.png")
                 .exampleImage("Center", "layout-ColumnLayout-start-center.png")
@@ -263,8 +355,10 @@
                 .exampleImage("SpaceAround", "layout-ColumnLayout-start-space-around.png")
                 .exampleImage("SpaceBetween", "layout-ColumnLayout-start-space-between.png")
                 .field(INT, "COMPONENT_ID", "unique id for this component")
-                .field(INT, "ANIMATION_ID", "id used to match components,"
-                        + " for animation purposes")
+                .field(
+                        INT,
+                        "ANIMATION_ID",
+                        "id used to match components," + " for animation purposes")
                 .field(INT, "HORIZONTAL_POSITIONING", "horizontal positioning value")
                 .possibleValues("START", ColumnLayout.START)
                 .possibleValues("CENTER", ColumnLayout.CENTER)
@@ -281,7 +375,12 @@
 
     @Override
     public void write(WireBuffer buffer) {
-        apply(buffer, mComponentId, mAnimationId,
-                mHorizontalPositioning, mVerticalPositioning, mSpacedBy);
+        apply(
+                buffer,
+                mComponentId,
+                mAnimationId,
+                mHorizontalPositioning,
+                mVerticalPositioning,
+                mSpacedBy);
     }
 }
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/LayoutManager.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/LayoutManager.java
index 3a36617..308ed64 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/LayoutManager.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/LayoutManager.java
@@ -24,53 +24,58 @@
 import com.android.internal.widget.remotecompose.core.operations.layout.measure.MeasurePass;
 import com.android.internal.widget.remotecompose.core.operations.layout.measure.Size;
 
-/**
- * Base class for layout managers -- resizable components.
- */
+/** Base class for layout managers -- resizable components. */
 public abstract class LayoutManager extends LayoutComponent implements Measurable {
 
     Size mCachedWrapSize = new Size(0f, 0f);
 
-    public LayoutManager(Component parent, int componentId, int animationId,
-                         float x, float y, float width, float height) {
+    public LayoutManager(
+            Component parent,
+            int componentId,
+            int animationId,
+            float x,
+            float y,
+            float width,
+            float height) {
         super(parent, componentId, animationId, x, y, width, height);
     }
 
-    /**
-     * Implemented by subclasses to provide a layout/measure pass
-     */
-    public void internalLayoutMeasure(PaintContext context,
-                                      MeasurePass measure) {
+    /** Implemented by subclasses to provide a layout/measure pass */
+    public void internalLayoutMeasure(PaintContext context, MeasurePass measure) {
         // nothing here
     }
 
-    /**
-     * Subclasses can implement this to provide wrap sizing
-     */
-    public void computeWrapSize(PaintContext context, float maxWidth, float maxHeight,
-                                MeasurePass measure, Size size) {
+    /** Subclasses can implement this to provide wrap sizing */
+    public void computeWrapSize(
+            PaintContext context, float maxWidth, float maxHeight, MeasurePass measure, Size size) {
         // nothing here
     }
 
-    /**
-     * Subclasses can implement this when not in wrap sizing
-     */
-    public void computeSize(PaintContext context, float minWidth, float maxWidth,
-                            float minHeight, float maxHeight, MeasurePass measure) {
+    /** Subclasses can implement this when not in wrap sizing */
+    public void computeSize(
+            PaintContext context,
+            float minWidth,
+            float maxWidth,
+            float minHeight,
+            float maxHeight,
+            MeasurePass measure) {
         // nothing here
     }
 
-    /**
-     * Base implementation of the measure resolution
-     */
+    /** Base implementation of the measure resolution */
     @Override
-    public void measure(PaintContext context, float minWidth, float maxWidth,
-                        float minHeight, float maxHeight, MeasurePass measure) {
+    public void measure(
+            PaintContext context,
+            float minWidth,
+            float maxWidth,
+            float minHeight,
+            float maxHeight,
+            MeasurePass measure) {
         boolean hasWrap = true;
-        float measuredWidth = Math.min(maxWidth,
-                computeModifierDefinedWidth() - mMarginLeft - mMarginRight);
-        float measuredHeight = Math.min(maxHeight,
-                computeModifierDefinedHeight() - mMarginTop - mMarginBottom);
+        float measuredWidth =
+                Math.min(maxWidth, computeModifierDefinedWidth() - mMarginLeft - mMarginRight);
+        float measuredHeight =
+                Math.min(maxHeight, computeModifierDefinedHeight() - mMarginTop - mMarginBottom);
         float insetMaxWidth = maxWidth - mMarginLeft - mMarginRight;
         float insetMaxHeight = maxHeight - mMarginTop - mMarginBottom;
         if (mWidthModifier.isWrap() || mHeightModifier.isWrap()) {
@@ -129,9 +134,7 @@
         internalLayoutMeasure(context, measure);
     }
 
-    /**
-     * basic layout of internal components
-     */
+    /** basic layout of internal components */
     @Override
     public void layout(RemoteContext context, MeasurePass measure) {
         super.layout(context, measure);
@@ -143,4 +146,18 @@
         }
         this.mNeedsMeasure = false;
     }
+
+    /**
+     * Only layout self, not children
+     *
+     * @param context
+     * @param measure
+     */
+    public void selfLayout(RemoteContext context, MeasurePass measure) {
+        super.layout(context, measure);
+        ComponentMeasure self = measure.get(this);
+
+        mComponentModifiers.layout(context, self.getW(), self.getH());
+        this.mNeedsMeasure = false;
+    }
 }
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/RowLayout.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/RowLayout.java
index 5e452f3..b29a05c 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/RowLayout.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/RowLayout.java
@@ -15,8 +15,8 @@
  */
 package com.android.internal.widget.remotecompose.core.operations.layout.managers;
 
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.FLOAT;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.FLOAT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
 
 import com.android.internal.widget.remotecompose.core.Operation;
 import com.android.internal.widget.remotecompose.core.Operations;
@@ -33,10 +33,7 @@
 
 import java.util.List;
 
-/**
- * Simple Row layout implementation
- * - also supports weight and horizontal/vertical positioning
- */
+/** Simple Row layout implementation - also supports weight and horizontal/vertical positioning */
 public class RowLayout extends LayoutManager implements ComponentStartOperation {
     public static final int START = 1;
     public static final int CENTER = 2;
@@ -51,68 +48,122 @@
     int mVerticalPositioning;
     float mSpacedBy = 0f;
 
-    public RowLayout(Component parent, int componentId, int animationId,
-                     float x, float y, float width, float height,
-                     int horizontalPositioning, int verticalPositioning, float spacedBy) {
+    public RowLayout(
+            Component parent,
+            int componentId,
+            int animationId,
+            float x,
+            float y,
+            float width,
+            float height,
+            int horizontalPositioning,
+            int verticalPositioning,
+            float spacedBy) {
         super(parent, componentId, animationId, x, y, width, height);
         mHorizontalPositioning = horizontalPositioning;
         mVerticalPositioning = verticalPositioning;
         mSpacedBy = spacedBy;
     }
 
-    public RowLayout(Component parent, int componentId, int animationId,
-                     int horizontalPositioning, int verticalPositioning, float spacedBy) {
-        this(parent, componentId, animationId, 0, 0, 0, 0,
-                horizontalPositioning, verticalPositioning, spacedBy);
+    public RowLayout(
+            Component parent,
+            int componentId,
+            int animationId,
+            int horizontalPositioning,
+            int verticalPositioning,
+            float spacedBy) {
+        this(
+                parent,
+                componentId,
+                animationId,
+                0,
+                0,
+                0,
+                0,
+                horizontalPositioning,
+                verticalPositioning,
+                spacedBy);
     }
 
     @Override
     public String toString() {
-        return "ROW [" + mComponentId + ":" + mAnimationId + "] (" + mX + ", "
-                + mY + " - " + mWidth + " x " + mHeight + ") " + mVisibility;
+        return "ROW ["
+                + mComponentId
+                + ":"
+                + mAnimationId
+                + "] ("
+                + mX
+                + ", "
+                + mY
+                + " - "
+                + mWidth
+                + " x "
+                + mHeight
+                + ") "
+                + mVisibility;
     }
 
+    @Override
     protected String getSerializedName() {
         return "ROW";
     }
 
     @Override
-    public void computeWrapSize(PaintContext context, float maxWidth, float maxHeight,
-                                MeasurePass measure, Size size) {
+    public void computeWrapSize(
+            PaintContext context, float maxWidth, float maxHeight, MeasurePass measure, Size size) {
         DebugLog.s(() -> "COMPUTE WRAP SIZE in " + this + " (" + mComponentId + ")");
+        //        int visibleChildrens = 0;
         for (Component c : mChildrenComponents) {
             c.measure(context, 0f, maxWidth, 0f, maxHeight, measure);
             ComponentMeasure m = measure.get(c);
-            size.setWidth(size.getWidth() + m.getW());
-            size.setHeight(Math.max(size.getHeight(), m.getH()));
+            if (m.getVisibility() != Visibility.GONE) {
+                size.setWidth(size.getWidth() + m.getW());
+                size.setHeight(Math.max(size.getHeight(), m.getH()));
+                //                visibleChildrens++;
+            }
         }
         if (!mChildrenComponents.isEmpty()) {
-            size.setWidth(size.getWidth()
-                    + (mSpacedBy * (mChildrenComponents.size() - 1)));
+            size.setWidth(size.getWidth() + (mSpacedBy * (mChildrenComponents.size() - 1)));
         }
         DebugLog.e();
     }
 
     @Override
-    public void computeSize(PaintContext context, float minWidth, float maxWidth,
-                            float minHeight, float maxHeight, MeasurePass measure) {
+    public void computeSize(
+            PaintContext context,
+            float minWidth,
+            float maxWidth,
+            float minHeight,
+            float maxHeight,
+            MeasurePass measure) {
         DebugLog.s(() -> "COMPUTE SIZE in " + this + " (" + mComponentId + ")");
         float mw = maxWidth;
         for (Component child : mChildrenComponents) {
             child.measure(context, minWidth, mw, minHeight, maxHeight, measure);
             ComponentMeasure m = measure.get(child);
-            mw -= m.getW();
+            if (m.getVisibility() != Visibility.GONE) {
+                mw -= m.getW();
+            }
         }
         DebugLog.e();
     }
 
     @Override
-    public void internalLayoutMeasure(PaintContext context,
-                                      MeasurePass measure) {
+    public void internalLayoutMeasure(PaintContext context, MeasurePass measure) {
         ComponentMeasure selfMeasure = measure.get(this);
-        DebugLog.s(() -> "INTERNAL LAYOUT " + this + " (" + mComponentId + ") children: "
-                + mChildrenComponents.size() + " size (" + selfMeasure.getW()
-                + " x " + selfMeasure.getH() + ")");
+        DebugLog.s(
+                () ->
+                        "INTERNAL LAYOUT "
+                                + this
+                                + " ("
+                                + mComponentId
+                                + ") children: "
+                                + mChildrenComponents.size()
+                                + " size ("
+                                + selfMeasure.getW()
+                                + " x "
+                                + selfMeasure.getH()
+                                + ")");
         if (mChildrenComponents.isEmpty()) {
             DebugLog.e();
             return;
@@ -126,6 +177,9 @@
         float totalWeights = 0f;
         for (Component child : mChildrenComponents) {
             ComponentMeasure childMeasure = measure.get(child);
+            if (childMeasure.getVisibility() == Visibility.GONE) {
+                continue;
+            }
             if (child instanceof LayoutComponent
                     && ((LayoutComponent) child).getWidthModifier().hasWeight()) {
                 hasWeights = true;
@@ -143,21 +197,34 @@
                 if (child instanceof LayoutComponent
                         && ((LayoutComponent) child).getWidthModifier().hasWeight()) {
                     ComponentMeasure childMeasure = measure.get(child);
+                    if (childMeasure.getVisibility() == Visibility.GONE) {
+                        continue;
+                    }
                     float weight = ((LayoutComponent) child).getWidthModifier().getValue();
                     childMeasure.setW((weight * availableSpace) / totalWeights);
-                    child.measure(context, childMeasure.getW(),
-                            childMeasure.getW(), childMeasure.getH(), childMeasure.getH(), measure);
+                    child.measure(
+                            context,
+                            childMeasure.getW(),
+                            childMeasure.getW(),
+                            childMeasure.getH(),
+                            childMeasure.getH(),
+                            measure);
                 }
             }
         }
 
         childrenWidth = 0f;
+        int visibleChildrens = 0;
         for (Component child : mChildrenComponents) {
             ComponentMeasure childMeasure = measure.get(child);
+            if (childMeasure.getVisibility() == Visibility.GONE) {
+                continue;
+            }
             childrenWidth += childMeasure.getW();
             childrenHeight = Math.max(childrenHeight, childMeasure.getH());
+            visibleChildrens++;
         }
-        childrenWidth += mSpacedBy * (mChildrenComponents.size() - 1);
+        childrenWidth += mSpacedBy * (visibleChildrens - 1);
 
         float tx = 0f;
         float ty = 0f;
@@ -178,24 +245,33 @@
             case SPACE_BETWEEN:
                 for (Component child : mChildrenComponents) {
                     ComponentMeasure childMeasure = measure.get(child);
+                    if (childMeasure.getVisibility() == Visibility.GONE) {
+                        continue;
+                    }
                     total += childMeasure.getW();
                 }
-                horizontalGap = (selfWidth - total) / (mChildrenComponents.size() - 1);
+                horizontalGap = (selfWidth - total) / (visibleChildrens - 1);
                 break;
             case SPACE_EVENLY:
                 for (Component child : mChildrenComponents) {
                     ComponentMeasure childMeasure = measure.get(child);
+                    if (childMeasure.getVisibility() == Visibility.GONE) {
+                        continue;
+                    }
                     total += childMeasure.getW();
                 }
-                horizontalGap = (selfWidth - total) / (mChildrenComponents.size() + 1);
+                horizontalGap = (selfWidth - total) / (visibleChildrens + 1);
                 tx = horizontalGap;
                 break;
             case SPACE_AROUND:
                 for (Component child : mChildrenComponents) {
                     ComponentMeasure childMeasure = measure.get(child);
+                    if (childMeasure.getVisibility() == Visibility.GONE) {
+                        continue;
+                    }
                     total += childMeasure.getW();
                 }
-                horizontalGap = (selfWidth - total) / (mChildrenComponents.size());
+                horizontalGap = (selfWidth - total) / visibleChildrens;
                 tx = horizontalGap / 2f;
                 break;
         }
@@ -215,6 +291,9 @@
             }
             childMeasure.setX(tx);
             childMeasure.setY(ty);
+            if (childMeasure.getVisibility() == Visibility.GONE) {
+                continue;
+            }
             tx += childMeasure.getW();
             if (mHorizontalPositioning == SPACE_BETWEEN
                     || mHorizontalPositioning == SPACE_AROUND
@@ -234,8 +313,13 @@
         return Operations.LAYOUT_ROW;
     }
 
-    public static void apply(WireBuffer buffer, int componentId, int animationId,
-                             int horizontalPositioning, int verticalPositioning, float spacedBy) {
+    public static void apply(
+            WireBuffer buffer,
+            int componentId,
+            int animationId,
+            int horizontalPositioning,
+            int verticalPositioning,
+            float spacedBy) {
         buffer.start(Operations.LAYOUT_ROW);
         buffer.writeInt(componentId);
         buffer.writeInt(animationId);
@@ -250,15 +334,22 @@
         int horizontalPositioning = buffer.readInt();
         int verticalPositioning = buffer.readInt();
         float spacedBy = buffer.readFloat();
-        operations.add(new RowLayout(null, componentId, animationId,
-                horizontalPositioning, verticalPositioning, spacedBy));
+        operations.add(
+                new RowLayout(
+                        null,
+                        componentId,
+                        animationId,
+                        horizontalPositioning,
+                        verticalPositioning,
+                        spacedBy));
     }
 
     public static void documentation(DocumentationBuilder doc) {
         doc.operation("Layout Operations", id(), name())
-                .description("Row layout implementation, positioning components one"
-                        + " after the other horizontally.\n\n"
-                        + "It supports weight and horizontal/vertical positioning.")
+                .description(
+                        "Row layout implementation, positioning components one"
+                                + " after the other horizontally.\n\n"
+                                + "It supports weight and horizontal/vertical positioning.")
                 .examplesDimension(400, 100)
                 .exampleImage("Start", "layout-RowLayout-start-top.png")
                 .exampleImage("Center", "layout-RowLayout-center-top.png")
@@ -267,8 +358,10 @@
                 .exampleImage("SpaceAround", "layout-RowLayout-space-around-top.png")
                 .exampleImage("SpaceBetween", "layout-RowLayout-space-between-top.png")
                 .field(INT, "COMPONENT_ID", "unique id for this component")
-                .field(INT, "ANIMATION_ID", "id used to match components,"
-                        + " for animation purposes")
+                .field(
+                        INT,
+                        "ANIMATION_ID",
+                        "id used to match components," + " for animation purposes")
                 .field(INT, "HORIZONTAL_POSITIONING", "horizontal positioning value")
                 .possibleValues("START", RowLayout.START)
                 .possibleValues("CENTER", RowLayout.CENTER)
@@ -285,7 +378,12 @@
 
     @Override
     public void write(WireBuffer buffer) {
-        apply(buffer, mComponentId, mAnimationId,
-                mHorizontalPositioning, mVerticalPositioning, mSpacedBy);
+        apply(
+                buffer,
+                mComponentId,
+                mAnimationId,
+                mHorizontalPositioning,
+                mVerticalPositioning,
+                mSpacedBy);
     }
 }
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/StateLayout.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/StateLayout.java
new file mode 100644
index 0000000..b5c7281
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/StateLayout.java
@@ -0,0 +1,565 @@
+/*
+ * 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.widget.remotecompose.core.operations.layout.managers;
+
+import com.android.internal.widget.remotecompose.core.CoreDocument;
+import com.android.internal.widget.remotecompose.core.Operation;
+import com.android.internal.widget.remotecompose.core.Operations;
+import com.android.internal.widget.remotecompose.core.PaintContext;
+import com.android.internal.widget.remotecompose.core.PaintOperation;
+import com.android.internal.widget.remotecompose.core.RemoteContext;
+import com.android.internal.widget.remotecompose.core.WireBuffer;
+import com.android.internal.widget.remotecompose.core.operations.layout.Component;
+import com.android.internal.widget.remotecompose.core.operations.layout.ComponentStartOperation;
+import com.android.internal.widget.remotecompose.core.operations.layout.LayoutComponent;
+import com.android.internal.widget.remotecompose.core.operations.layout.measure.ComponentMeasure;
+import com.android.internal.widget.remotecompose.core.operations.layout.measure.MeasurePass;
+import com.android.internal.widget.remotecompose.core.operations.layout.measure.Size;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * State-based animated layout
+ *
+ * <p>States are defined as child layouts. This layout handles interpolating between the different
+ * state in order to provide an automatic transition.
+ */
+public class StateLayout extends LayoutManager implements ComponentStartOperation {
+
+    public int measuredLayoutIndex = 0;
+    public int currentLayoutIndex = 0;
+    public int previousLayoutIndex = 0;
+    private int mIndexId = 0;
+
+    // This keep track of all the components associated with a given Id,
+    // (the key being the id), and the set of components corresponds to the set of states
+    // TODO: we should be able to optimize this
+    public Map<Integer, Component[]> statePaintedComponents = new HashMap<>();
+
+    public int MAX_CACHE_ELEMENTS = 16;
+    public int[] cacheListElementsId = new int[MAX_CACHE_ELEMENTS];
+
+    public boolean inTransition = false;
+
+    public StateLayout(
+            Component parent,
+            int componentId,
+            int animationId,
+            float x,
+            float y,
+            float width,
+            float height,
+            int indexId) {
+        super(parent, componentId, animationId, x, y, width, height);
+        //        if (layoutInfo.visibleLayoutIndex != null) {
+        //            layoutInfo.visibleLayoutIndex!!.addChangeListener(this)
+        //        }
+        mIndexId = indexId;
+    }
+
+    @Override
+    public void inflate() {
+        super.inflate();
+        hideLayoutsOtherThan(currentLayoutIndex);
+    }
+
+    public void findAnimatedComponents() {
+        for (int i = 0; i < mChildrenComponents.size(); i++) {
+            Component cs = mChildrenComponents.get(i);
+            if (cs instanceof LayoutComponent) {
+                LayoutComponent state = (LayoutComponent) cs;
+                state.setX(0f);
+                state.setY(0f);
+                ArrayList<Component> childrenComponents = state.getChildrenComponents();
+                for (int j = 0; j < childrenComponents.size(); j++) {
+                    Component child = childrenComponents.get(j);
+                    if (child.getAnimationId() != -1) {
+                        if (!statePaintedComponents.containsKey(child.getAnimationId())) {
+                            statePaintedComponents.put(
+                                    child.getAnimationId(),
+                                    new Component[mChildrenComponents.size()]);
+                        }
+                        statePaintedComponents.get(child.getAnimationId())[i] = child;
+                    }
+                }
+            }
+        }
+        collapsePaintedComponents();
+    }
+
+    public void collapsePaintedComponents() {
+        int numStates = mChildrenComponents.size();
+        for (Integer id : statePaintedComponents.keySet()) {
+            Component[] list = statePaintedComponents.get(id);
+            int numComponents = list.length;
+            if (numComponents > 1 && list[0] != null) {
+                Component c1 = list[0];
+                boolean same = true;
+                for (int i = 1; i < list.length; i++) {
+                    Component c2 = list[i];
+                    if (c2 == null || !c1.suitableForTransition(c2)) {
+                        same = false;
+                        break;
+                    }
+                }
+                if (same) {
+                    // TODO: Fix, shouldn't want to recopy all components
+                    for (int i = 0; i < numStates; i++) {
+                        list[i] = c1;
+                    }
+                }
+            }
+        }
+    }
+
+    @Override
+    public void computeSize(
+            PaintContext context,
+            float minWidth,
+            float maxWidth,
+            float minHeight,
+            float maxHeight,
+            MeasurePass measure) {
+        LayoutManager layout = getLayout(currentLayoutIndex);
+        layout.computeSize(context, minWidth, maxWidth, minHeight, maxHeight, measure);
+    }
+
+    @Override
+    public void internalLayoutMeasure(
+            PaintContext context,
+            // layoutInfo: LayoutInfo,
+            MeasurePass measure) {
+        LayoutManager layout = getLayout(currentLayoutIndex);
+        //        layout.internalLayoutMeasure(context, layoutInfo, measure)
+        layout.internalLayoutMeasure(context, measure);
+    }
+
+    /** Subclasses can implement this to provide wrap sizing */
+    @Override
+    public void computeWrapSize(
+            PaintContext context, float maxWidth, float maxHeight, MeasurePass measure, Size size) {
+        LayoutManager layout = getLayout(currentLayoutIndex);
+        layout.computeWrapSize(context, maxWidth, maxHeight, measure, size);
+    }
+
+    @Override
+    public void onClick(RemoteContext context, CoreDocument document, float x, float y) {
+        if (!contains(x, y)) {
+            return;
+        }
+        LayoutManager layout = getLayout(currentLayoutIndex);
+        layout.onClick(context, document, x, y);
+    }
+
+    @Override
+    public void layout(RemoteContext context, MeasurePass measure) {
+        ComponentMeasure self = measure.get(this);
+        super.selfLayout(context, measure);
+
+        // We can simply layout the current layout...
+        LayoutManager layout = getLayout(currentLayoutIndex);
+
+        // Pass through the measure information from the state layout to the currently
+        // selected component that this being laid out.
+        ComponentMeasure layoutMeasure = measure.get(layout.getComponentId());
+        layoutMeasure.copyFrom(self);
+
+        layout.layout(context, measure);
+
+        // but if we are in a transition, we might have to layout previous widgets
+        if (inTransition && previousLayoutIndex != currentLayoutIndex) {
+            LayoutManager previous = getLayout(previousLayoutIndex);
+            for (Component c : previous.getChildrenComponents()) {
+                int id = c.getComponentId();
+                if (c.getAnimationId() != -1) {
+                    id = c.getAnimationId();
+                    Component[] rc = statePaintedComponents.get(id);
+                    for (Component ac : rc) {
+                        if (ac != null) {
+                            ac.layout(context, measure);
+                        }
+                    }
+                }
+                if (measure.contains(id)) {
+                    c.layout(context, measure);
+                }
+            }
+        }
+
+        mFirstLayout = false;
+    }
+
+    @Override
+    public void measure(
+            PaintContext context,
+            float minWidth,
+            float maxWidth,
+            float minHeight,
+            float maxHeight,
+            MeasurePass measure) {
+        // The general approach for this widget is to do most of the work/setup in measure.
+        // layout and paint then simply use what's been setup in the measure phase.
+
+        // First, let's initialize the statePaintedComponents array;
+        // it contains for each animation id a set of components associated, one for each state.
+        // For now to keep things simple, all components sets have the same size (== number of
+        // states)
+        if (statePaintedComponents.isEmpty()) {
+            findAnimatedComponents();
+        }
+
+        // TODO : FIRST LAYOUT ANIMATE THE GONE ELEMENT, RESIZING IT. We should be able to fix it
+        // if we resize things before animting.
+
+        LayoutManager layout = getLayout(currentLayoutIndex);
+
+        // ok so *before* we do the layout, we should make sure to set the *new* widgets (that
+        // share the same id) to be at the same bounds / position as the current displayed ones
+        if (inTransition && currentLayoutIndex != previousLayoutIndex) {
+            LayoutManager previousLayout = getLayout(previousLayoutIndex);
+            for (Component c : layout.getChildrenComponents()) {
+                int id = c.getAnimationId();
+                if (id == -1) {
+                    continue;
+                }
+                for (Component pc : previousLayout.getChildrenComponents()) {
+                    if (pc.getAnimationId() == id) {
+                        Component prev =
+                                statePaintedComponents.get(c.getAnimationId())[previousLayoutIndex];
+                        if (c != prev) {
+                            c.measure(
+                                    context,
+                                    prev.getWidth(),
+                                    prev.getWidth(),
+                                    prev.getHeight(),
+                                    prev.getHeight(),
+                                    measure);
+                            c.layout(context.getContext(), measure);
+                            c.setX(prev.getX());
+                            c.setY(prev.getY());
+                            c.mVisibility = Visibility.GONE;
+                        }
+                        break;
+                    }
+                }
+            }
+        }
+
+        // Alright, now that things are set in place, let's go ahead and measure the new world...
+        layout.measure(context, minWidth, maxWidth, minHeight, maxHeight, measure);
+
+        // recopy to animationIds the values
+        for (Component c : layout.getChildrenComponents()) {
+            ComponentMeasure cm = measure.get(c);
+            if (c.getAnimationId() != -1) {
+                // First, we grab the current component for an animation id, and get its measure,
+                // then set this measure to the measure for the animation id
+                ComponentMeasure m = measure.get(c.getAnimationId());
+                m.copyFrom(cm);
+
+                m.setVisibility(Visibility.VISIBLE);
+
+                // Then for each components sharing the id in all the states...
+                Component[] components = statePaintedComponents.get(c.getAnimationId());
+                for (int idx = 0; idx < components.length; idx++) {
+                    Component ac = components[idx];
+                    if (ac != null) {
+                        ComponentMeasure m2 = measure.get(ac.getComponentId());
+
+                        // ... we set their measures to be the measure of the current component
+                        if (c != ac) {
+                            m2.copyFrom(cm);
+                        }
+
+                        // Finally let's make sure that for all components we set their visibility
+                        if (idx == currentLayoutIndex) {
+                            m2.setVisibility(Visibility.VISIBLE);
+                        } else {
+                            if (c != ac) {
+                                m2.setVisibility(Visibility.GONE);
+                            }
+                        }
+
+                        // if the component isn't the current one, we should measure it
+                        if (c != ac) {
+                            ac.measure(context, m.getW(), m.getW(), m.getH(), m.getH(), measure);
+                        }
+                    }
+                }
+            } else {
+                // TODO: Ideally unify the visibility handing so that we also work in terms of
+                // component and not panel visibility. Ideally do not change the .visibility
+                // attribute at all and actually use the "current index" to decide whether to
+                // draw or not.
+                cm.setVisibility(Visibility.VISIBLE);
+            }
+        }
+
+        // Make sure to mark the components that are not in the new layout as being GONE
+        if (previousLayoutIndex != currentLayoutIndex) {
+            LayoutManager previousLayout = getLayout(previousLayoutIndex);
+            for (Component c : previousLayout.getChildrenComponents()) {
+                int id = c.getComponentId();
+                if (c.getAnimationId() != -1) {
+                    id = c.getAnimationId();
+                }
+                if (!measure.contains(id)) {
+                    ComponentMeasure m = measure.get(c.getComponentId());
+                    m.setX(c.getX());
+                    m.setY(c.getY());
+                    m.setW(c.getWidth());
+                    m.setH(c.getHeight());
+                    m.setVisibility(Visibility.GONE);
+                }
+            }
+        }
+
+        ComponentMeasure m = measure.get(layout);
+        ComponentMeasure own = measure.get(this);
+        own.copyFrom(m);
+        measuredLayoutIndex = currentLayoutIndex;
+    }
+
+    public void hideLayoutsOtherThan(int idx) {
+        int index = 0;
+        for (Component pane : mChildrenComponents) {
+            if (pane instanceof LayoutComponent) {
+                if (index != idx) {
+                    pane.mVisibility = Visibility.GONE;
+                } else {
+                    pane.mVisibility = Visibility.VISIBLE;
+                }
+                index++;
+            }
+        }
+    }
+
+    public LayoutManager getLayout(int idx) {
+        int index = 0;
+        for (Component pane : mChildrenComponents) {
+            if (pane instanceof LayoutComponent) {
+                if (index == idx) {
+                    return (LayoutManager) pane;
+                }
+                index++;
+            }
+        }
+        return (LayoutManager) mChildrenComponents.get(0);
+    }
+
+    @Override
+    public void paint(PaintContext context) {
+        if (mIndexId != 0) {
+            int newValue = context.getContext().mRemoteComposeState.getInteger(mIndexId);
+            if (newValue != currentLayoutIndex) {
+                previousLayoutIndex = currentLayoutIndex;
+                currentLayoutIndex = newValue;
+                inTransition = true;
+                System.out.println("currentLayout index is $currentLayoutIndex");
+                // executeValueSetActions(getLayout(currentLayoutIndex));
+                invalidateMeasure();
+            }
+        }
+        System.out.println("PAINTING LAYOUT STATELAYOUT, CURRENT INDEX " + currentLayoutIndex);
+        // Make sure to mark any components that are not in either the current or previous layout
+        // as being GONE.
+        int index = 0;
+        for (Component pane : mChildrenComponents) {
+            if (pane instanceof LayoutComponent) {
+                if (index != currentLayoutIndex && index != previousLayoutIndex) {
+                    pane.mVisibility = Visibility.GONE;
+                }
+                if (index == currentLayoutIndex && pane.mVisibility != Visibility.VISIBLE) {
+                    pane.mVisibility = Visibility.VISIBLE;
+                }
+                index++;
+            }
+        }
+
+        LayoutManager currentLayout = getLayout(measuredLayoutIndex);
+        boolean needsToPaintTransition = inTransition && previousLayoutIndex != measuredLayoutIndex;
+        if (needsToPaintTransition) {
+            // in case we have switched to a new state, during the transition
+            // we might still need to display the previous components that are not part of
+            // the new state (to enable them to run their exit animation)
+
+            LayoutManager previousLayout = getLayout(previousLayoutIndex);
+            int numPreviousComponents = previousLayout.getChildrenComponents().size();
+            if (numPreviousComponents > MAX_CACHE_ELEMENTS) {
+                MAX_CACHE_ELEMENTS *= 2;
+                cacheListElementsId = new int[MAX_CACHE_ELEMENTS];
+            }
+            // Make sure to apply the animation if there...
+            previousLayout.applyAnimationAsNeeded(context);
+
+            // Let's grab all the ids for the components of the previous layout...
+            int idIndex = 0;
+            for (Component c : previousLayout.getChildrenComponents()) {
+                cacheListElementsId[idIndex] = c.getPaintId();
+                idIndex++;
+            }
+            // ...then remove them if they are in the new layout
+            int count = idIndex;
+            for (Component c : currentLayout.getChildrenComponents()) {
+                int id = c.getPaintId();
+                for (int i = 0; i < idIndex; i++) {
+                    if (cacheListElementsId[i] == id) {
+                        cacheListElementsId[i] = -1;
+                        count--;
+                    }
+                }
+            }
+            // If we have components not present in the new state, paint them
+            if (count > 0) {
+                context.save();
+                context.translate(previousLayout.getX(), previousLayout.getY());
+                for (Component c : previousLayout.getChildrenComponents()) {
+                    int id = c.getPaintId();
+                    for (int i = 0; i < idIndex; i++) {
+                        if (cacheListElementsId[i] == id) {
+                            context.translate(
+                                    previousLayout.getMarginLeft(), previousLayout.getMarginTop());
+                            c.paint(context);
+                            context.translate(
+                                    -currentLayout.getMarginLeft(), -currentLayout.getMarginTop());
+                            break;
+                        }
+                    }
+                }
+                context.restore();
+            }
+
+            // Make sure to apply the animation if there...
+            currentLayout.applyAnimationAsNeeded(context);
+        }
+
+        // We paint all the components and operations of the current layout
+        context.save();
+        context.translate(currentLayout.getX(), currentLayout.getY());
+        for (Operation op : currentLayout.getList()) {
+            if (op instanceof Component && ((Component) op).getAnimationId() != -1) {
+                Component[] stateComponents =
+                        statePaintedComponents.get(((Component) op).getAnimationId());
+                Component component = stateComponents[measuredLayoutIndex];
+                if (needsToPaintTransition) {
+                    // We might have two components to paint, as in case two different
+                    // components share the same id, we'll fade the previous components out
+                    // and fade in the new one
+                    Component previousComponent = stateComponents[previousLayoutIndex];
+                    if (previousComponent != null && component != previousComponent) {
+                        context.translate(
+                                currentLayout.getMarginLeft(), currentLayout.getMarginTop());
+                        previousComponent.paint(context);
+                        context.translate(
+                                -currentLayout.getMarginLeft(), -currentLayout.getMarginTop());
+                    }
+                }
+                context.translate(currentLayout.getMarginLeft(), currentLayout.getMarginTop());
+                component.paint(context);
+                context.translate(-currentLayout.getMarginLeft(), -currentLayout.getMarginTop());
+            } else if (op instanceof PaintOperation) {
+                ((PaintOperation) op).paint(context);
+            }
+        }
+        context.restore();
+
+        if (needsToPaintTransition) {
+            checkEndOfTransition();
+        }
+    }
+
+    public void checkEndOfTransition() {
+        LayoutManager currentLayout = getLayout(measuredLayoutIndex);
+        LayoutManager previousLayout = getLayout(previousLayoutIndex);
+        if (inTransition
+                && currentLayout.mAnimateMeasure == null
+                && previousLayout.mAnimateMeasure == null) {
+            inTransition = false;
+            LayoutManager previous = getLayout(previousLayoutIndex);
+            if (previous != currentLayout && previous.mVisibility != Visibility.GONE) {
+                previous.mVisibility = Visibility.GONE;
+                previous.needsRepaint();
+            }
+        }
+    }
+
+    //    override fun onValueChanged(origamiValue: OrigamiValue<out Int>, oldValue: Int?, newValue:
+    // Int) {
+    //        if (newValue != currentLayoutIndex) {
+    //            previousLayoutIndex = currentLayoutIndex
+    //            currentLayoutIndex = newValue
+    //            inTransition = true
+    //            println("currentLayout index is $currentLayoutIndex")
+    //            executeValueSetActions(getLayout(currentLayoutIndex))
+    //            invalidateMeasure()
+    //        }
+    //    }
+
+    //    fun executeValueSetActions(layout: LayoutManager) {
+    //        // FIXME : quick hack to support ValueSetClickActions, need to make that a little more
+    //        // robust!
+    //        for (op in layout.list) {
+    //            if (op is LayoutComponent) {
+    //                for (op2 in op.list) {
+    //                    if (op2 is OperationsList) {
+    //                        for (op3 in op2.list) {
+    //                            if (op3 is ValueSetClickAction<*, *>) {
+    //                                op3.onClick()
+    //                            }
+    //                        }
+    //                    }
+    //                }
+    //            }
+    //        }
+    //    }
+
+    @Override
+    public String toString() {
+        return "STATE_LAYOUT";
+    }
+
+    //    companion object {
+    //        fun documentation(doc: OrigamiDocumentation) {}
+    //    }
+
+    public static void apply(
+            WireBuffer buffer,
+            int componentId,
+            int animationId,
+            int horizontalPositioning,
+            int verticalPositioning,
+            int indexId) {
+        buffer.start(Operations.LAYOUT_STATE);
+        buffer.writeInt(componentId);
+        buffer.writeInt(animationId);
+        buffer.writeInt(horizontalPositioning);
+        buffer.writeInt(verticalPositioning);
+        buffer.writeInt(indexId);
+    }
+
+    public static void read(WireBuffer buffer, List<Operation> operations) {
+        int componentId = buffer.readInt();
+        int animationId = buffer.readInt();
+        buffer.readInt(); // horizontalPositioning
+        buffer.readInt(); // verticalPositioning
+        int indexId = buffer.readInt();
+        operations.add(
+                new StateLayout(null, componentId, animationId, 0f, 0f, 100f, 100f, indexId));
+    }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/TextLayout.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/TextLayout.java
index 2370500..c1cabcd 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/TextLayout.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/TextLayout.java
@@ -15,8 +15,8 @@
  */
 package com.android.internal.widget.remotecompose.core.operations.layout.managers;
 
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.FLOAT;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.FLOAT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
 
 import com.android.internal.widget.remotecompose.core.Operation;
 import com.android.internal.widget.remotecompose.core.Operations;
@@ -34,9 +34,7 @@
 
 import java.util.List;
 
-/**
- * Text component, referencing a text id
- */
+/** Text component, referencing a text id */
 public class TextLayout extends LayoutManager implements ComponentStartOperation, VariableSupport {
 
     private static final boolean DEBUG = false;
@@ -86,10 +84,20 @@
         needsRepaint();
     }
 
-    public TextLayout(Component parent, int componentId, int animationId,
-                      float x, float y, float width, float height,
-                      int textId, int color, float fontSize,
-                      int fontStyle, float fontWeight, int fontFamilyId) {
+    public TextLayout(
+            Component parent,
+            int componentId,
+            int animationId,
+            float x,
+            float y,
+            float width,
+            float height,
+            int textId,
+            int color,
+            float fontSize,
+            int fontStyle,
+            float fontWeight,
+            int fontFamilyId) {
         super(parent, componentId, animationId, x, y, width, height);
         mTextId = textId;
         mColor = color;
@@ -99,11 +107,30 @@
         mFontFamilyId = fontFamilyId;
     }
 
-    public TextLayout(Component parent, int componentId, int animationId,
-                      int textId, int color, float fontSize,
-                      int fontStyle, float fontWeight, int fontFamilyId) {
-        this(parent, componentId, animationId, 0, 0, 0, 0,
-                textId, color, fontSize, fontStyle, fontWeight, fontFamilyId);
+    public TextLayout(
+            Component parent,
+            int componentId,
+            int animationId,
+            int textId,
+            int color,
+            float fontSize,
+            int fontStyle,
+            float fontWeight,
+            int fontFamilyId) {
+        this(
+                parent,
+                componentId,
+                animationId,
+                0,
+                0,
+                0,
+                0,
+                textId,
+                color,
+                fontSize,
+                fontStyle,
+                fontWeight,
+                fontFamilyId);
     }
 
     public PaintBundle mPaint = new PaintBundle();
@@ -151,26 +178,57 @@
 
     @Override
     public String toString() {
-        return "TEXT_LAYOUT [" + mComponentId + ":" + mAnimationId + "] (" + mX + ", "
-                + mY + " - " + mWidth + " x " + mHeight + ") " + mVisibility;
+        return "TEXT_LAYOUT ["
+                + mComponentId
+                + ":"
+                + mAnimationId
+                + "] ("
+                + mX
+                + ", "
+                + mY
+                + " - "
+                + mWidth
+                + " x "
+                + mHeight
+                + ") "
+                + mVisibility;
     }
 
+    @Override
     protected String getSerializedName() {
         return "TEXT_LAYOUT";
     }
 
     @Override
     public void serializeToString(int indent, StringSerializer serializer) {
-        serializer.append(indent, getSerializedName() + " [" + mComponentId
-                + ":" + mAnimationId + "] = "
-                + "[" + mX + ", " + mY + ", " + mWidth + ", " + mHeight + "] "
-                + mVisibility + " (" + mTextId + ":\"" + mCachedString + "\")"
-        );
+        serializer.append(
+                indent,
+                getSerializedName()
+                        + " ["
+                        + mComponentId
+                        + ":"
+                        + mAnimationId
+                        + "] = "
+                        + "["
+                        + mX
+                        + ", "
+                        + mY
+                        + ", "
+                        + mWidth
+                        + ", "
+                        + mHeight
+                        + "] "
+                        + mVisibility
+                        + " ("
+                        + mTextId
+                        + ":\""
+                        + mCachedString
+                        + "\")");
     }
 
     @Override
-    public void computeWrapSize(PaintContext context, float maxWidth, float maxHeight,
-                                MeasurePass measure, Size size) {
+    public void computeWrapSize(
+            PaintContext context, float maxWidth, float maxHeight, MeasurePass measure, Size size) {
         context.savePaint();
         mPaint.reset();
         mPaint.setTextSize(mFontSize);
@@ -196,9 +254,16 @@
         return Operations.LAYOUT_TEXT;
     }
 
-    public static void apply(WireBuffer buffer, int componentId, int animationId,
-                             int textId, int color, float fontSize, int fontStyle,
-                             float fontWeight, int fontFamilyId) {
+    public static void apply(
+            WireBuffer buffer,
+            int componentId,
+            int animationId,
+            int textId,
+            int color,
+            float fontSize,
+            int fontStyle,
+            float fontWeight,
+            int fontFamilyId) {
         buffer.start(id());
         buffer.writeInt(componentId);
         buffer.writeInt(animationId);
@@ -219,16 +284,27 @@
         int fontStyle = buffer.readInt();
         float fontWeight = buffer.readFloat();
         int fontFamilyId = buffer.readInt();
-        operations.add(new TextLayout(null, componentId, animationId, textId, color, fontSize,
-                fontStyle, fontWeight, fontFamilyId));
+        operations.add(
+                new TextLayout(
+                        null,
+                        componentId,
+                        animationId,
+                        textId,
+                        color,
+                        fontSize,
+                        fontStyle,
+                        fontWeight,
+                        fontFamilyId));
     }
 
     public static void documentation(DocumentationBuilder doc) {
         doc.operation("Layout Operations", id(), name())
                 .description("Text layout implementation.\n\n")
                 .field(INT, "COMPONENT_ID", "unique id for this component")
-                .field(INT, "ANIMATION_ID", "id used to match components,"
-                        + " for animation purposes")
+                .field(
+                        INT,
+                        "ANIMATION_ID",
+                        "id used to match components," + " for animation purposes")
                 .field(INT, "COLOR", "text color")
                 .field(FLOAT, "FONT_SIZE", "font size")
                 .field(INT, "FONT_STYLE", "font style (0 = normal, 1 = italic)")
@@ -238,8 +314,15 @@
 
     @Override
     public void write(WireBuffer buffer) {
-        apply(buffer, mComponentId, mAnimationId,
-                mTextId, mColor, mFontSize, mFontStyle,
-                mFontWeight, mFontFamilyId);
+        apply(
+                buffer,
+                mComponentId,
+                mAnimationId,
+                mTextId,
+                mColor,
+                mFontSize,
+                mFontStyle,
+                mFontWeight,
+                mFontFamilyId);
     }
 }
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/measure/ComponentMeasure.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/measure/ComponentMeasure.java
index 8dc10d5..285425f 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/measure/ComponentMeasure.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/measure/ComponentMeasure.java
@@ -13,13 +13,11 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
 package com.android.internal.widget.remotecompose.core.operations.layout.measure;
+
 import com.android.internal.widget.remotecompose.core.operations.layout.Component;
 
-/**
- * Encapsulate the result of a measure pass for a component
- */
+/** Encapsulate the result of a measure pass for a component */
 public class ComponentMeasure {
     int mId = -1;
     float mX;
@@ -31,24 +29,31 @@
     public void setX(float value) {
         mX = value;
     }
+
     public void setY(float value) {
         mY = value;
     }
+
     public void setW(float value) {
         mW = value;
     }
+
     public void setH(float value) {
         mH = value;
     }
+
     public float getX() {
         return mX;
     }
+
     public float getY() {
         return mY;
     }
+
     public float getW() {
         return mW;
     }
+
     public float getH() {
         return mH;
     }
@@ -61,8 +66,8 @@
         mVisibility = visibility;
     }
 
-    public ComponentMeasure(int id, float x, float y, float w, float h,
-                            Component.Visibility visibility) {
+    public ComponentMeasure(
+            int id, float x, float y, float w, float h, Component.Visibility visibility) {
         this.mId = id;
         this.mX = x;
         this.mY = y;
@@ -76,8 +81,12 @@
     }
 
     public ComponentMeasure(Component component) {
-        this(component.getComponentId(), component.getX(), component.getY(),
-                component.getWidth(), component.getHeight(),
+        this(
+                component.getComponentId(),
+                component.getX(),
+                component.getY(),
+                component.getWidth(),
+                component.getHeight(),
                 component.mVisibility);
     }
 
@@ -88,4 +97,8 @@
         mH = m.mH;
         mVisibility = m.mVisibility;
     }
+
+    public boolean same(ComponentMeasure m) {
+        return mX == m.mX && mY == m.mY && mW == m.mW && mH == m.mH && mVisibility == m.mVisibility;
+    }
 }
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/measure/Measurable.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/measure/Measurable.java
index d167d9b..b48c2d5 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/measure/Measurable.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/measure/Measurable.java
@@ -13,33 +13,33 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
 package com.android.internal.widget.remotecompose.core.operations.layout.measure;
 
 import com.android.internal.widget.remotecompose.core.PaintContext;
 import com.android.internal.widget.remotecompose.core.RemoteContext;
 
-/**
- * Interface describing the measure/layout contract for components
- */
+/** Interface describing the measure/layout contract for components */
 public interface Measurable {
 
     /**
-     * Measure a component and store the result of the measure in the provided MeasurePass.
-     * This does not apply the measure to the component.
+     * Measure a component and store the result of the measure in the provided MeasurePass. This
+     * does not apply the measure to the component.
      */
-    void measure(PaintContext context, float minWidth, float maxWidth,
-                 float minHeight, float maxHeight, MeasurePass measure);
+    void measure(
+            PaintContext context,
+            float minWidth,
+            float maxWidth,
+            float minHeight,
+            float maxHeight,
+            MeasurePass measure);
 
-    /**
-     * Apply a given measure to the component
-     */
+    /** Apply a given measure to the component */
     void layout(RemoteContext context, MeasurePass measure);
 
     /**
      * Return true if the component needs to be remeasured
+     *
      * @return true if need to remeasured, false otherwise
      */
     boolean needsMeasure();
-
 }
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/measure/MeasurePass.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/measure/MeasurePass.java
index 6801deb..8d01fea 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/measure/MeasurePass.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/measure/MeasurePass.java
@@ -20,8 +20,8 @@
 import java.util.HashMap;
 
 /**
- * Represents the result of a measure pass on the entire hierarchy
- * TODO: optimize to use a flat array vs the current hashmap
+ * Represents the result of a measure pass on the entire hierarchy TODO: optimize to use a flat
+ * array vs the current hashmap
  */
 public class MeasurePass {
     HashMap<Integer, ComponentMeasure> mList = new HashMap<>();
@@ -43,8 +43,9 @@
 
     public ComponentMeasure get(Component c) {
         if (!mList.containsKey(c.getComponentId())) {
-            ComponentMeasure measure = new ComponentMeasure(c.getComponentId(),
-                    c.getX(), c.getY(), c.getWidth(), c.getHeight());
+            ComponentMeasure measure =
+                    new ComponentMeasure(
+                            c.getComponentId(), c.getX(), c.getY(), c.getWidth(), c.getHeight());
             mList.put(c.getComponentId(), measure);
             return measure;
         }
@@ -53,8 +54,8 @@
 
     public ComponentMeasure get(int id) {
         if (!mList.containsKey(id)) {
-            ComponentMeasure measure = new ComponentMeasure(id,
-                    0f, 0f, 0f, 0f, Component.Visibility.GONE);
+            ComponentMeasure measure =
+                    new ComponentMeasure(id, 0f, 0f, 0f, 0f, Component.Visibility.GONE);
             mList.put(id, measure);
             return measure;
         }
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/measure/Size.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/measure/Size.java
index b11d8e8..53f4a71 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/measure/Size.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/measure/Size.java
@@ -15,12 +15,11 @@
  */
 package com.android.internal.widget.remotecompose.core.operations.layout.measure;
 
-/**
- * Basic data class representing a component size, used during layout computations.
- */
+/** Basic data class representing a component size, used during layout computations. */
 public class Size {
     float mWidth;
     float mHeight;
+
     public Size(float width, float height) {
         this.mWidth = width;
         this.mHeight = height;
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/BackgroundModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/BackgroundModifierOperation.java
index f3e6a8e..64e40f7 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/BackgroundModifierOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/BackgroundModifierOperation.java
@@ -15,7 +15,7 @@
  */
 package com.android.internal.widget.remotecompose.core.operations.layout.modifiers;
 
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.FLOAT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.FLOAT;
 
 import com.android.internal.widget.remotecompose.core.Operation;
 import com.android.internal.widget.remotecompose.core.Operations;
@@ -28,9 +28,7 @@
 
 import java.util.List;
 
-/**
- * Component size-aware background draw
- */
+/** Component size-aware background draw */
 public class BackgroundModifierOperation extends DecoratorModifierOperation {
     private static final int OP_CODE = Operations.MODIFIER_BACKGROUND;
     private static final String CLASS_NAME = "BackgroundModifierOperation";
@@ -46,9 +44,16 @@
 
     public PaintBundle mPaint = new PaintBundle();
 
-    public BackgroundModifierOperation(float x, float y, float width, float height,
-                                       float r, float g, float b, float a,
-                                       int shapeType) {
+    public BackgroundModifierOperation(
+            float x,
+            float y,
+            float width,
+            float height,
+            float r,
+            float g,
+            float b,
+            float a,
+            int shapeType) {
         this.mX = x;
         this.mY = y;
         this.mWidth = width;
@@ -67,10 +72,27 @@
 
     @Override
     public void serializeToString(int indent, StringSerializer serializer) {
-        serializer.append(indent, "BACKGROUND = [" + mX + ", "
-                + mY + ", " + mWidth + ", " + mHeight
-                + "] color [" + mR + ", " + mG + ", " + mB + ", " + mA
-                + "] shape [" + mShapeType + "]");
+        serializer.append(
+                indent,
+                "BACKGROUND = ["
+                        + mX
+                        + ", "
+                        + mY
+                        + ", "
+                        + mWidth
+                        + ", "
+                        + mHeight
+                        + "] color ["
+                        + mR
+                        + ", "
+                        + mG
+                        + ", "
+                        + mB
+                        + ", "
+                        + mA
+                        + "] shape ["
+                        + mShapeType
+                        + "]");
     }
 
     @Override
@@ -92,8 +114,17 @@
         return OP_CODE;
     }
 
-    public static void apply(WireBuffer buffer, float x, float y, float width, float height,
-                             float r, float g, float b, float a, int shapeType) {
+    public static void apply(
+            WireBuffer buffer,
+            float x,
+            float y,
+            float width,
+            float height,
+            float r,
+            float g,
+            float b,
+            float a,
+            int shapeType) {
         buffer.start(OP_CODE);
         buffer.writeFloat(x);
         buffer.writeFloat(y);
@@ -118,11 +149,9 @@
         float a = buffer.readFloat();
         // shape type
         int shapeType = buffer.readInt();
-        operations.add(new BackgroundModifierOperation(x, y, width, height,
-                r, g, b, a, shapeType));
+        operations.add(new BackgroundModifierOperation(x, y, width, height, r, g, b, a, shapeType));
     }
 
-
     @Override
     public void paint(PaintContext context) {
         context.savePaint();
@@ -133,16 +162,13 @@
         if (mShapeType == ShapeType.RECTANGLE) {
             context.drawRect(0f, 0f, mWidth, mHeight);
         } else if (mShapeType == ShapeType.CIRCLE) {
-            context.drawCircle(mWidth / 2f, mHeight / 2f,
-                    Math.min(mWidth, mHeight) / 2f);
+            context.drawCircle(mWidth / 2f, mHeight / 2f, Math.min(mWidth, mHeight) / 2f);
         }
         context.restorePaint();
     }
 
     public static void documentation(DocumentationBuilder doc) {
-        doc.operation("Modifier Operations",
-                        OP_CODE,
-                        CLASS_NAME)
+        doc.operation("Modifier Operations", OP_CODE, CLASS_NAME)
                 .description("define the Background Modifier")
                 .field(FLOAT, "x", "")
                 .field(FLOAT, "y", "")
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/BorderModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/BorderModifierOperation.java
index 4c83ec4..92c0a73 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/BorderModifierOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/BorderModifierOperation.java
@@ -15,7 +15,7 @@
  */
 package com.android.internal.widget.remotecompose.core.operations.layout.modifiers;
 
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.FLOAT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.FLOAT;
 
 import com.android.internal.widget.remotecompose.core.Operation;
 import com.android.internal.widget.remotecompose.core.Operations;
@@ -28,9 +28,7 @@
 
 import java.util.List;
 
-/**
- * Component size-aware border draw
- */
+/** Component size-aware border draw */
 public class BorderModifierOperation extends DecoratorModifierOperation {
     private static final int OP_CODE = Operations.MODIFIER_BORDER;
     public static final String CLASS_NAME = "BorderModifierOperation";
@@ -49,9 +47,18 @@
 
     public PaintBundle paint = new PaintBundle();
 
-    public BorderModifierOperation(float x, float y, float width, float height,
-                                   float borderWidth, float roundedCorner,
-                                   float r, float g, float b, float a, int shapeType) {
+    public BorderModifierOperation(
+            float x,
+            float y,
+            float width,
+            float height,
+            float borderWidth,
+            float roundedCorner,
+            float r,
+            float g,
+            float b,
+            float a,
+            int shapeType) {
         this.mX = x;
         this.mY = y;
         this.mWidth = width;
@@ -67,17 +74,51 @@
 
     @Override
     public void serializeToString(int indent, StringSerializer serializer) {
-        serializer.append(indent, "BORDER = [" + mX + ", " + mY + ", "
-                + mWidth + ", " + mHeight + "] "
-                + "color [" + mR + ", " + mG + ", " + mB + ", " + mA + "] "
-                + "border [" + mBorderWidth + ", " + mRoundedCorner + "] "
-                + "shape [" + mShapeType + "]");
+        serializer.append(
+                indent,
+                "BORDER = ["
+                        + mX
+                        + ", "
+                        + mY
+                        + ", "
+                        + mWidth
+                        + ", "
+                        + mHeight
+                        + "] "
+                        + "color ["
+                        + mR
+                        + ", "
+                        + mG
+                        + ", "
+                        + mB
+                        + ", "
+                        + mA
+                        + "] "
+                        + "border ["
+                        + mBorderWidth
+                        + ", "
+                        + mRoundedCorner
+                        + "] "
+                        + "shape ["
+                        + mShapeType
+                        + "]");
     }
 
     @Override
     public void write(WireBuffer buffer) {
-        apply(buffer, mX, mY, mWidth, mHeight, mBorderWidth, mRoundedCorner,
-                mR, mG, mB, mA, mShapeType);
+        apply(
+                buffer,
+                mX,
+                mY,
+                mWidth,
+                mHeight,
+                mBorderWidth,
+                mRoundedCorner,
+                mR,
+                mG,
+                mB,
+                mA,
+                mShapeType);
     }
 
     @Override
@@ -88,12 +129,29 @@
 
     @Override
     public String toString() {
-        return "BorderModifierOperation(" + mX + "," + mY + " - " + mWidth + " x " + mHeight + ") "
-                + "borderWidth(" + mBorderWidth + ") "
-                + "color(" + mR + "," + mG + "," + mB + "," + mA + ")";
+        return "BorderModifierOperation("
+                + mX
+                + ","
+                + mY
+                + " - "
+                + mWidth
+                + " x "
+                + mHeight
+                + ") "
+                + "borderWidth("
+                + mBorderWidth
+                + ") "
+                + "color("
+                + mR
+                + ","
+                + mG
+                + ","
+                + mB
+                + ","
+                + mA
+                + ")";
     }
 
-
     public static String name() {
         return CLASS_NAME;
     }
@@ -102,10 +160,19 @@
         return OP_CODE;
     }
 
-    public static void apply(WireBuffer buffer, float x, float y, float width, float height,
-                             float borderWidth, float roundedCorner,
-                             float r, float g, float b, float a,
-                             int shapeType) {
+    public static void apply(
+            WireBuffer buffer,
+            float x,
+            float y,
+            float width,
+            float height,
+            float borderWidth,
+            float roundedCorner,
+            float r,
+            float g,
+            float b,
+            float a,
+            int shapeType) {
         buffer.start(OP_CODE);
         buffer.writeFloat(x);
         buffer.writeFloat(y);
@@ -134,11 +201,10 @@
         float a = buffer.readFloat();
         // shape type
         int shapeType = buffer.readInt();
-        operations.add(new BorderModifierOperation(x, y, width, height, bw,
-                rc, r, g, b, a, shapeType));
+        operations.add(
+                new BorderModifierOperation(x, y, width, height, bw, rc, r, g, b, a, shapeType));
     }
 
-
     @Override
     public void paint(PaintContext context) {
         context.savePaint();
@@ -160,9 +226,7 @@
     }
 
     public static void documentation(DocumentationBuilder doc) {
-        doc.operation("Modifier Operations",
-                        OP_CODE,
-                        CLASS_NAME)
+        doc.operation("Modifier Operations", OP_CODE, CLASS_NAME)
                 .description("define the Border Modifier")
                 .field(FLOAT, "x", "")
                 .field(FLOAT, "y", "")
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ClipRectModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ClipRectModifierOperation.java
index 7cb7925..0d8aeaa 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ClipRectModifierOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ClipRectModifierOperation.java
@@ -27,9 +27,7 @@
 
 import java.util.List;
 
-/**
- * Support modifier clip with a rectangle
- */
+/** Support modifier clip with a rectangle */
 public class ClipRectModifierOperation extends DecoratorModifierOperation {
     public static final String CLASS_NAME = "ClipRectModifierOperation";
     private static final int OP_CODE = Operations.MODIFIER_CLIP_RECT;
@@ -48,15 +46,14 @@
     }
 
     @Override
-    public void onClick(RemoteContext context, CoreDocument document,
-                        Component component, float x, float y) {
+    public void onClick(
+            RemoteContext context, CoreDocument document, Component component, float x, float y) {
         // nothing
     }
 
     @Override
     public void serializeToString(int indent, StringSerializer serializer) {
-        serializer.append(
-                indent, "CLIP_RECT = [" + mWidth + ", " + mHeight + "]");
+        serializer.append(indent, "CLIP_RECT = [" + mWidth + ", " + mHeight + "]");
     }
 
     @Override
@@ -68,7 +65,6 @@
         return CLASS_NAME;
     }
 
-
     public static int id() {
         return OP_CODE;
     }
@@ -77,16 +73,12 @@
         buffer.start(OP_CODE);
     }
 
-
     public static void read(WireBuffer buffer, List<Operation> operations) {
         operations.add(new ClipRectModifierOperation());
     }
 
-
     public static void documentation(DocumentationBuilder doc) {
-        doc.operation("Canvas Operations",
-                        OP_CODE,
-                        CLASS_NAME)
+        doc.operation("Canvas Operations", OP_CODE, CLASS_NAME)
                 .description("Draw the specified round-rect");
     }
 }
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ComponentModifiers.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ComponentModifiers.java
index f55c941..95786a8 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ComponentModifiers.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ComponentModifiers.java
@@ -29,9 +29,7 @@
 
 import java.util.ArrayList;
 
-/**
- * Maintain a list of modifiers
- */
+/** Maintain a list of modifiers */
 public class ComponentModifiers extends PaintOperation implements DecoratorComponent {
     ArrayList<ModifierOperation> mList = new ArrayList<>();
 
@@ -49,7 +47,7 @@
 
     @Override
     public String toString() {
-        String str =  "ComponentModifiers \n";
+        String str = "ComponentModifiers \n";
         for (ModifierOperation modifierOperation : mList) {
             str += "    " + modifierOperation.toString() + "\n";
         }
@@ -125,8 +123,9 @@
         mList.addAll(operations);
     }
 
-    public void onClick(RemoteContext context, CoreDocument document,
-                        Component component, float x, float y) {
+    @Override
+    public void onClick(
+            RemoteContext context, CoreDocument document, Component component, float x, float y) {
         for (ModifierOperation op : mList) {
             if (op instanceof DecoratorComponent) {
                 ((DecoratorComponent) op).onClick(context, document, component, x, y);
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ComponentVisibilityOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ComponentVisibilityOperation.java
index 9c19073..312d016 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ComponentVisibilityOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ComponentVisibilityOperation.java
@@ -15,7 +15,7 @@
  */
 package com.android.internal.widget.remotecompose.core.operations.layout.modifiers;
 
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
 
 import com.android.internal.widget.remotecompose.core.CoreDocument;
 import com.android.internal.widget.remotecompose.core.Operation;
@@ -31,11 +31,9 @@
 
 import java.util.List;
 
-/**
- * Allows setting visibility on a component
- */
-public class ComponentVisibilityOperation implements ModifierOperation,
-        VariableSupport, DecoratorComponent {
+/** Allows setting visibility on a component */
+public class ComponentVisibilityOperation
+        implements ModifierOperation, VariableSupport, DecoratorComponent {
     private static final int OP_CODE = Operations.MODIFIER_VISIBILITY;
 
     int mVisibilityId;
@@ -61,19 +59,15 @@
     }
 
     @Override
-    public void apply(RemoteContext context) {
-    }
+    public void apply(RemoteContext context) {}
 
     @Override
     public String deepToString(String indent) {
         return (indent != null ? indent : "") + toString();
     }
 
-
     @Override
-    public void write(WireBuffer buffer) {
-
-    }
+    public void write(WireBuffer buffer) {}
 
     public static void apply(WireBuffer buffer, int valueId) {
         buffer.start(OP_CODE);
@@ -87,8 +81,9 @@
 
     public static void documentation(DocumentationBuilder doc) {
         doc.operation("Layout Operations", OP_CODE, "ComponentVisibility")
-                .description("This operation allows setting a component"
-                        + "visibility from a provided value")
+                .description(
+                        "This operation allows setting a component"
+                                + "visibility from a provided value")
                 .field(INT, "VALUE_ID", "Value ID representing the visibility");
     }
 
@@ -119,13 +114,9 @@
     }
 
     @Override
-    public void layout(RemoteContext context, float width, float height) {
-
-    }
+    public void layout(RemoteContext context, float width, float height) {}
 
     @Override
-    public void onClick(RemoteContext context, CoreDocument document,
-                        Component component, float x, float y) {
-
-    }
+    public void onClick(
+            RemoteContext context, CoreDocument document, Component component, float x, float y) {}
 }
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/DecoratorModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/DecoratorModifierOperation.java
index 70a5728..41e18cb 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/DecoratorModifierOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/DecoratorModifierOperation.java
@@ -22,15 +22,15 @@
 import com.android.internal.widget.remotecompose.core.operations.layout.DecoratorComponent;
 
 /**
- * Represents a decorator modifier (lightweight component), ie a modifier
- * that impacts the visual output (background, border...)
+ * Represents a decorator modifier (lightweight component), ie a modifier that impacts the visual
+ * output (background, border...)
  */
 public abstract class DecoratorModifierOperation extends PaintOperation
         implements ModifierOperation, DecoratorComponent {
 
     @Override
-    public void onClick(RemoteContext context, CoreDocument document,
-                        Component component, float x, float y) {
+    public void onClick(
+            RemoteContext context, CoreDocument document, Component component, float x, float y) {
         // nothing
     }
 }
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/DimensionModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/DimensionModifierOperation.java
index f085ffb..408bebc 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/DimensionModifierOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/DimensionModifierOperation.java
@@ -20,13 +20,16 @@
 import com.android.internal.widget.remotecompose.core.operations.Utils;
 import com.android.internal.widget.remotecompose.core.operations.utilities.StringSerializer;
 
-/**
- * Base class for dimension modifiers
- */
+/** Base class for dimension modifiers */
 public abstract class DimensionModifierOperation implements ModifierOperation, VariableSupport {
 
     public enum Type {
-        EXACT, FILL, WRAP, WEIGHT, INTRINSIC_MIN, INTRINSIC_MAX;
+        EXACT,
+        FILL,
+        WRAP,
+        WEIGHT,
+        INTRINSIC_MIN,
+        INTRINSIC_MAX;
 
         static Type fromInt(int value) {
             switch (value) {
@@ -67,10 +70,8 @@
     @Override
     public void updateVariables(RemoteContext context) {
         if (mType == Type.EXACT) {
-            mOutValue = (Float.isNaN(mValue))
-                    ? context.getFloat(Utils.idFromNan(mValue)) : mValue;
+            mOutValue = Float.isNaN(mValue) ? context.getFloat(Utils.idFromNan(mValue)) : mValue;
         }
-
     }
 
     @Override
@@ -80,10 +81,8 @@
                 context.listensTo(Utils.idFromNan(mValue), this);
             }
         }
-
     }
 
-
     public boolean hasWeight() {
         return mType == Type.WEIGHT;
     }
@@ -108,7 +107,6 @@
         mOutValue = mValue = value;
     }
 
-
     public String serializedName() {
         return "DIMENSION";
     }
@@ -121,8 +119,7 @@
     }
 
     @Override
-    public void apply(RemoteContext context) {
-    }
+    public void apply(RemoteContext context) {}
 
     @Override
     public String deepToString(String indent) {
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HeightModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HeightModifierOperation.java
index a0f576a..d3613f8 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HeightModifierOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HeightModifierOperation.java
@@ -15,8 +15,8 @@
  */
 package com.android.internal.widget.remotecompose.core.operations.layout.modifiers;
 
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.FLOAT;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.FLOAT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
 
 import com.android.internal.widget.remotecompose.core.Operation;
 import com.android.internal.widget.remotecompose.core.Operations;
@@ -25,9 +25,7 @@
 
 import java.util.List;
 
-/**
- * Set the height dimension on a component
- */
+/** Set the height dimension on a component */
 public class HeightModifierOperation extends DimensionModifierOperation {
     private static final int OP_CODE = Operations.MODIFIER_HEIGHT;
     public static final String CLASS_NAME = "HeightModifierOperation";
@@ -81,9 +79,7 @@
     }
 
     public static void documentation(DocumentationBuilder doc) {
-        doc.operation("Modifier Operations",
-                        OP_CODE,
-                        CLASS_NAME)
+        doc.operation("Modifier Operations", OP_CODE, CLASS_NAME)
                 .description("define the animation")
                 .field(INT, "type", "")
                 .field(FLOAT, "value", "");
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HostActionOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HostActionOperation.java
index d405b2b..ac42470a 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HostActionOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HostActionOperation.java
@@ -15,7 +15,7 @@
  */
 package com.android.internal.widget.remotecompose.core.operations.layout.modifiers;
 
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
 
 import com.android.internal.widget.remotecompose.core.CoreDocument;
 import com.android.internal.widget.remotecompose.core.Operation;
@@ -29,9 +29,7 @@
 
 import java.util.List;
 
-/**
- * Capture a host action information. This can be triggered on eg. a click.
- */
+/** Capture a host action information. This can be triggered on eg. a click. */
 public class HostActionOperation implements ActionOperation {
     private static final int OP_CODE = Operations.HOST_ACTION;
 
@@ -60,23 +58,19 @@
     }
 
     @Override
-    public void apply(RemoteContext context) {
-    }
+    public void apply(RemoteContext context) {}
 
     @Override
     public String deepToString(String indent) {
         return (indent != null ? indent : "") + toString();
     }
 
+    @Override
+    public void write(WireBuffer buffer) {}
 
     @Override
-    public void write(WireBuffer buffer) {
-
-    }
-
-    @Override
-    public void runAction(RemoteContext context, CoreDocument document,
-                          Component component, float x, float y) {
+    public void runAction(
+            RemoteContext context, CoreDocument document, Component component, float x, float y) {
         context.runAction(mActionId, "");
     }
 
@@ -95,5 +89,4 @@
                 .description("Host action. This operation represents a host action")
                 .field(INT, "ACTION_ID", "Host Action ID");
     }
-
 }
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HostNamedActionOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HostNamedActionOperation.java
index 35f202b..b674a58 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HostNamedActionOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HostNamedActionOperation.java
@@ -15,7 +15,7 @@
  */
 package com.android.internal.widget.remotecompose.core.operations.layout.modifiers;
 
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
 
 import com.android.internal.widget.remotecompose.core.CoreDocument;
 import com.android.internal.widget.remotecompose.core.Operation;
@@ -29,9 +29,7 @@
 
 import java.util.List;
 
-/**
- * Capture a host action information. This can be triggered on eg. a click.
- */
+/** Capture a host action information. This can be triggered on eg. a click. */
 public class HostNamedActionOperation implements ActionOperation {
     private static final int OP_CODE = Operations.HOST_NAMED_ACTION;
 
@@ -56,23 +54,19 @@
     }
 
     @Override
-    public void apply(RemoteContext context) {
-    }
+    public void apply(RemoteContext context) {}
 
     @Override
     public String deepToString(String indent) {
         return (indent != null ? indent : "") + toString();
     }
 
+    @Override
+    public void write(WireBuffer buffer) {}
 
     @Override
-    public void write(WireBuffer buffer) {
-
-    }
-
-    @Override
-    public void runAction(RemoteContext context, CoreDocument document,
-                          Component component, float x, float y) {
+    public void runAction(
+            RemoteContext context, CoreDocument document, Component component, float x, float y) {
         context.runNamedAction(mTextId);
     }
 
@@ -91,5 +85,4 @@
                 .description("Host Named action. This operation represents a host action")
                 .field(INT, "TEXT_ID", "Named Host Action Text ID");
     }
-
 }
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ModifierOperation.java
index 5299719..50f098e 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ModifierOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ModifierOperation.java
@@ -18,9 +18,7 @@
 import com.android.internal.widget.remotecompose.core.Operation;
 import com.android.internal.widget.remotecompose.core.operations.utilities.StringSerializer;
 
-/**
- * Represents a modifier
- */
+/** Represents a modifier */
 public interface ModifierOperation extends Operation {
     void serializeToString(int indent, StringSerializer serializer);
 }
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/PaddingModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/PaddingModifierOperation.java
index 668db3b..e0ec1a6 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/PaddingModifierOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/PaddingModifierOperation.java
@@ -15,7 +15,7 @@
  */
 package com.android.internal.widget.remotecompose.core.operations.layout.modifiers;
 
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.FLOAT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.FLOAT;
 
 import com.android.internal.widget.remotecompose.core.Operation;
 import com.android.internal.widget.remotecompose.core.Operations;
@@ -27,8 +27,8 @@
 import java.util.List;
 
 /**
- * Represents a padding modifier.
- * Padding modifiers can be chained and will impact following modifiers.
+ * Represents a padding modifier. Padding modifiers can be chained and will impact following
+ * modifiers.
  */
 public class PaddingModifierOperation implements ModifierOperation {
     private static final int OP_CODE = Operations.MODIFIER_PADDING;
@@ -84,13 +84,12 @@
 
     @Override
     public void serializeToString(int indent, StringSerializer serializer) {
-        serializer.append(indent, "PADDING = [" + mLeft + ", " + mTop + ", "
-                + mRight + ", " + mBottom + "]");
+        serializer.append(
+                indent, "PADDING = [" + mLeft + ", " + mTop + ", " + mRight + ", " + mBottom + "]");
     }
 
     @Override
-    public void apply(RemoteContext context) {
-    }
+    public void apply(RemoteContext context) {}
 
     @Override
     public String deepToString(String indent) {
@@ -99,8 +98,15 @@
 
     @Override
     public String toString() {
-        return "PaddingModifierOperation(" + mLeft + ", " + mTop
-                + ", " + mRight + ", " + mBottom + ")";
+        return "PaddingModifierOperation("
+                + mLeft
+                + ", "
+                + mTop
+                + ", "
+                + mRight
+                + ", "
+                + mBottom
+                + ")";
     }
 
     public static String name() {
@@ -111,8 +117,7 @@
         return Operations.MODIFIER_PADDING;
     }
 
-    public static void apply(WireBuffer buffer,
-                             float left, float top, float right, float bottom) {
+    public static void apply(WireBuffer buffer, float left, float top, float right, float bottom) {
         buffer.start(Operations.MODIFIER_PADDING);
         buffer.writeFloat(left);
         buffer.writeFloat(top);
@@ -129,9 +134,7 @@
     }
 
     public static void documentation(DocumentationBuilder doc) {
-        doc.operation("Modifier Operations",
-                        OP_CODE,
-                        CLASS_NAME)
+        doc.operation("Modifier Operations", OP_CODE, CLASS_NAME)
                 .description("define the Padding Modifier")
                 .field(FLOAT, "left", "")
                 .field(FLOAT, "top", "")
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/RoundedClipRectModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/RoundedClipRectModifierOperation.java
index 9b662bf5..dc95fe7 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/RoundedClipRectModifierOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/RoundedClipRectModifierOperation.java
@@ -15,7 +15,7 @@
  */
 package com.android.internal.widget.remotecompose.core.operations.layout.modifiers;
 
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.FLOAT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.FLOAT;
 
 import com.android.internal.widget.remotecompose.core.CoreDocument;
 import com.android.internal.widget.remotecompose.core.Operation;
@@ -31,9 +31,7 @@
 
 import java.util.List;
 
-/**
- * Support clip with a rounded rectangle
- */
+/** Support clip with a rounded rectangle */
 public class RoundedClipRectModifierOperation extends DrawBase4
         implements ModifierOperation, DecoratorComponent {
     public static final int OP_CODE = Operations.MODIFIER_ROUNDED_CLIP_RECT;
@@ -52,44 +50,41 @@
         return CLASS_NAME;
     }
 
-
-    protected void write(WireBuffer buffer,
-                         float v1,
-                         float v2,
-                         float v3,
-                         float v4) {
+    @Override
+    protected void write(WireBuffer buffer, float v1, float v2, float v3, float v4) {
         apply(buffer, v1, v2, v3, v4);
     }
 
     public static void documentation(DocumentationBuilder doc) {
-        doc.operation("Modifier Operations",
-                        id(),
-                        "RoundedClipRectModifierOperation")
+        doc.operation("Modifier Operations", id(), "RoundedClipRectModifierOperation")
                 .description("clip with rectangle")
-                .field(FLOAT, "topStart",
+                .field(
+                        FLOAT,
+                        "topStart",
                         "The topStart radius of the rectangle to "
                                 + "intersect with the current clip")
-                .field(FLOAT, "topEnd",
+                .field(
+                        FLOAT,
+                        "topEnd",
                         "The topEnd radius of the rectangle to "
                                 + "intersect with the current clip")
-                .field(FLOAT, "bottomStart",
+                .field(
+                        FLOAT,
+                        "bottomStart",
                         "The bottomStart radius of the rectangle to "
                                 + "intersect with the current clip")
-                .field(FLOAT, "bottomEnd",
+                .field(
+                        FLOAT,
+                        "bottomEnd",
                         "The bottomEnd radius of the rectangle to "
                                 + "intersect with the current clip");
     }
 
-
     float mWidth;
     float mHeight;
 
-
     public RoundedClipRectModifierOperation(
-            float topStart,
-            float topEnd,
-            float bottomStart,
-            float bottomEnd) {
+            float topStart, float topEnd, float bottomStart, float bottomEnd) {
         super(topStart, topEnd, bottomStart, bottomEnd);
         mName = CLASS_NAME;
     }
@@ -106,33 +101,41 @@
     }
 
     @Override
-    public void onClick(RemoteContext context, CoreDocument document,
-                        Component component, float x, float y) {
+    public void onClick(
+            RemoteContext context, CoreDocument document, Component component, float x, float y) {
         // nothing
     }
 
     @Override
     public void serializeToString(int indent, StringSerializer serializer) {
         serializer.append(
-                indent, "ROUNDED_CLIP_RECT = [" + mWidth + ", " + mHeight
-                        + ", " + mX1 + ", " + mY1
-                        + ", " + mX2 + ", " + mY2 + "]");
+                indent,
+                "ROUNDED_CLIP_RECT = ["
+                        + mWidth
+                        + ", "
+                        + mHeight
+                        + ", "
+                        + mX1
+                        + ", "
+                        + mY1
+                        + ", "
+                        + mX2
+                        + ", "
+                        + mY2
+                        + "]");
     }
 
     /**
      * Writes out the rounded rect clip to the buffer
      *
-     * @param buffer        buffer to write to
-     * @param topStart      topStart radius
-     * @param topEnd        topEnd radius
-     * @param bottomStart   bottomStart radius
-     * @param bottomEnd     bottomEnd radius
+     * @param buffer buffer to write to
+     * @param topStart topStart radius
+     * @param topEnd topEnd radius
+     * @param bottomStart bottomStart radius
+     * @param bottomEnd bottomEnd radius
      */
-    public static void apply(WireBuffer buffer,
-                             float topStart,
-                             float topEnd,
-                             float bottomStart,
-                             float bottomEnd) {
+    public static void apply(
+            WireBuffer buffer, float topStart, float topEnd, float bottomStart, float bottomEnd) {
         write(buffer, OP_CODE, topStart, topEnd, bottomStart, bottomEnd);
     }
 }
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ShapeType.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ShapeType.java
index e425b4e..0cd062e 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ShapeType.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ShapeType.java
@@ -15,9 +15,7 @@
  */
 package com.android.internal.widget.remotecompose.core.operations.layout.modifiers;
 
-/**
- * Known shapes, used for modifiers (clip/background/border)
- */
+/** Known shapes, used for modifiers (clip/background/border) */
 public class ShapeType {
     public static int RECTANGLE = 0;
     public static int CIRCLE = 1;
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueIntegerChangeActionOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueIntegerChangeActionOperation.java
index 3f19c9b..8876720 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueIntegerChangeActionOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueIntegerChangeActionOperation.java
@@ -15,7 +15,7 @@
  */
 package com.android.internal.widget.remotecompose.core.operations.layout.modifiers;
 
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
 
 import com.android.internal.widget.remotecompose.core.CoreDocument;
 import com.android.internal.widget.remotecompose.core.Operation;
@@ -29,9 +29,7 @@
 
 import java.util.List;
 
-/**
- * Apply a value change on an integer variable.
- */
+/** Apply a value change on an integer variable. */
 public class ValueIntegerChangeActionOperation implements ActionOperation {
     private static final int OP_CODE = Operations.VALUE_INTEGER_CHANGE_ACTION;
 
@@ -54,28 +52,23 @@
 
     @Override
     public void serializeToString(int indent, StringSerializer serializer) {
-        serializer.append(indent, serializedName()
-                + " = " + mTargetValueId + " -> " + mValue);
+        serializer.append(indent, serializedName() + " = " + mTargetValueId + " -> " + mValue);
     }
 
     @Override
-    public void apply(RemoteContext context) {
-    }
+    public void apply(RemoteContext context) {}
 
     @Override
     public String deepToString(String indent) {
         return (indent != null ? indent : "") + toString();
     }
 
+    @Override
+    public void write(WireBuffer buffer) {}
 
     @Override
-    public void write(WireBuffer buffer) {
-
-    }
-
-    @Override
-    public void runAction(RemoteContext context, CoreDocument document,
-                          Component component, float x, float y) {
+    public void runAction(
+            RemoteContext context, CoreDocument document, Component component, float x, float y) {
         context.overrideInteger(mTargetValueId, mValue);
     }
 
@@ -93,11 +86,10 @@
 
     public static void documentation(DocumentationBuilder doc) {
         doc.operation("Layout Operations", OP_CODE, "ValueIntegerChangeActionOperation")
-                .description("ValueIntegerChange action. "
-                        + " This operation represents a value change for the given id")
+                .description(
+                        "ValueIntegerChange action. "
+                                + " This operation represents a value change for the given id")
                 .field(INT, "TARGET_VALUE_ID", "Value ID")
-                .field(INT, "VALUE", "integer value to be assigned to the target")
-        ;
+                .field(INT, "VALUE", "integer value to be assigned to the target");
     }
-
 }
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueIntegerExpressionChangeActionOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueIntegerExpressionChangeActionOperation.java
new file mode 100644
index 0000000..fb5e911
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueIntegerExpressionChangeActionOperation.java
@@ -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.internal.widget.remotecompose.core.operations.layout.modifiers;
+
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
+
+import com.android.internal.widget.remotecompose.core.CoreDocument;
+import com.android.internal.widget.remotecompose.core.Operation;
+import com.android.internal.widget.remotecompose.core.Operations;
+import com.android.internal.widget.remotecompose.core.RemoteContext;
+import com.android.internal.widget.remotecompose.core.WireBuffer;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.operations.layout.ActionOperation;
+import com.android.internal.widget.remotecompose.core.operations.layout.Component;
+import com.android.internal.widget.remotecompose.core.operations.utilities.StringSerializer;
+
+import java.util.List;
+
+/** Apply a value change on an integer variable. */
+public class ValueIntegerExpressionChangeActionOperation implements ActionOperation {
+    private static final int OP_CODE = Operations.VALUE_INTEGER_EXPRESSION_CHANGE_ACTION;
+
+    long mTargetValueId = -1;
+    long mValueExpressionId = -1;
+
+    public ValueIntegerExpressionChangeActionOperation(long id, long value) {
+        mTargetValueId = id;
+        mValueExpressionId = value;
+    }
+
+    @Override
+    public String toString() {
+        return "ValueIntegerExpressionChangeActionOperation(" + mTargetValueId + ")";
+    }
+
+    public String serializedName() {
+        return "VALUE_INTEGER_EXPRESSION_CHANGE";
+    }
+
+    @Override
+    public void serializeToString(int indent, StringSerializer serializer) {
+        serializer.append(
+                indent, serializedName() + " = " + mTargetValueId + " -> " + mValueExpressionId);
+    }
+
+    @Override
+    public void apply(RemoteContext context) {}
+
+    @Override
+    public String deepToString(String indent) {
+        return (indent != null ? indent : "") + toString();
+    }
+
+    @Override
+    public void write(WireBuffer buffer) {}
+
+    @Override
+    public void runAction(
+            RemoteContext context, CoreDocument document, Component component, float x, float y) {
+        document.evaluateIntExpression(mValueExpressionId, (int) mTargetValueId, context);
+    }
+
+    public static void apply(WireBuffer buffer, long valueId, long value) {
+        buffer.start(OP_CODE);
+        buffer.writeLong(valueId);
+        buffer.writeLong(value);
+    }
+
+    public static void read(WireBuffer buffer, List<Operation> operations) {
+        long valueId = buffer.readLong();
+        long value = buffer.readLong();
+        operations.add(new ValueIntegerExpressionChangeActionOperation(valueId, value));
+    }
+
+    public static void documentation(DocumentationBuilder doc) {
+        doc.operation("Layout Operations", OP_CODE, "ValueIntegerExpressionChangeActionOperation")
+                .description(
+                        "ValueIntegerExpressionChange action. "
+                                + " This operation represents a value change for the given id")
+                .field(INT, "TARGET_VALUE_ID", "Value ID")
+                .field(INT, "VALUE_ID", "id of the value to be assigned to the target");
+    }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueStringChangeActionOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueStringChangeActionOperation.java
index e2542e5..a64a492 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueStringChangeActionOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueStringChangeActionOperation.java
@@ -15,7 +15,7 @@
  */
 package com.android.internal.widget.remotecompose.core.operations.layout.modifiers;
 
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
 
 import com.android.internal.widget.remotecompose.core.CoreDocument;
 import com.android.internal.widget.remotecompose.core.Operation;
@@ -29,9 +29,7 @@
 
 import java.util.List;
 
-/**
- * Apply a value change on a string variable.
- */
+/** Apply a value change on a string variable. */
 public class ValueStringChangeActionOperation implements ActionOperation {
     private static final int OP_CODE = Operations.VALUE_STRING_CHANGE_ACTION;
 
@@ -62,8 +60,7 @@
     }
 
     @Override
-    public void apply(RemoteContext context) {
-    }
+    public void apply(RemoteContext context) {}
 
     @Override
     public String deepToString(String indent) {
@@ -71,13 +68,11 @@
     }
 
     @Override
-    public void write(WireBuffer buffer) {
-
-    }
+    public void write(WireBuffer buffer) {}
 
     @Override
-    public void runAction(RemoteContext context, CoreDocument document,
-                          Component component, float x, float y) {
+    public void runAction(
+            RemoteContext context, CoreDocument document, Component component, float x, float y) {
         context.overrideText(mTargetValueId, mValueId);
     }
 
@@ -95,13 +90,14 @@
 
     public static void documentation(DocumentationBuilder doc) {
         doc.operation("Layout Operations", OP_CODE, "ValueStringChangeActionOperation")
-                .description("ValueStrin gChange action. "
-                        + " This operation represents a String change (referenced by id) "
-                        + "for the given string id")
+                .description(
+                        "ValueStrin gChange action. "
+                                + " This operation represents a String change (referenced by id) "
+                                + "for the given string id")
                 .field(INT, "TARGET_ID", "Target Value ID")
-                .field(INT, "VALUE_ID", "Value ID to be assigned to the target "
-                        + "value as a string")
-        ;
+                .field(
+                        INT,
+                        "VALUE_ID",
+                        "Value ID to be assigned to the target " + "value as a string");
     }
-
 }
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/WidthModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/WidthModifierOperation.java
index 7fd5e51..62403b3 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/WidthModifierOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/WidthModifierOperation.java
@@ -15,8 +15,8 @@
  */
 package com.android.internal.widget.remotecompose.core.operations.layout.modifiers;
 
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.FLOAT;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.FLOAT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
 
 import com.android.internal.widget.remotecompose.core.Operation;
 import com.android.internal.widget.remotecompose.core.Operations;
@@ -25,14 +25,11 @@
 
 import java.util.List;
 
-/**
- * Set the width dimension on a component
- */
+/** Set the width dimension on a component */
 public class WidthModifierOperation extends DimensionModifierOperation {
     private static final int OP_CODE = Operations.MODIFIER_WIDTH;
     public static final String CLASS_NAME = "WidthModifierOperation";
 
-
     public static String name() {
         return CLASS_NAME;
     }
@@ -82,9 +79,7 @@
     }
 
     public static void documentation(DocumentationBuilder doc) {
-        doc.operation("Modifier Operations",
-                        OP_CODE,
-                        CLASS_NAME)
+        doc.operation("Modifier Operations", OP_CODE, CLASS_NAME)
                 .description("define the animation")
                 .field(INT, "type", "")
                 .field(FLOAT, "value", "");
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/utils/DebugLog.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/utils/DebugLog.java
index 7ccf7f4..4849b12 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/utils/DebugLog.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/utils/DebugLog.java
@@ -17,9 +17,7 @@
 
 import java.util.ArrayList;
 
-/**
- * Internal utility debug class
- */
+/** Internal utility debug class */
 public class DebugLog {
 
     public static final boolean DEBUG_LAYOUT_ON = false;
@@ -109,8 +107,12 @@
                 if (node instanceof LogNode) {
                     builder.append(indentation).append("     ").append(node.name).append("\n");
                 } else {
-                    builder.append(indentation).append("-- ").append(node.name)
-                            .append(" : ").append(node.endString).append("\n");
+                    builder.append(indentation)
+                            .append("-- ")
+                            .append(node.name)
+                            .append(" : ")
+                            .append(node.endString)
+                            .append("\n");
                 }
             }
         }
@@ -124,4 +126,3 @@
         }
     }
 }
-
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/utils/StringValueSupplier.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/utils/StringValueSupplier.java
index 79ef16b..701167a 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/utils/StringValueSupplier.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/utils/StringValueSupplier.java
@@ -15,12 +15,11 @@
  */
 package com.android.internal.widget.remotecompose.core.operations.layout.utils;
 
-/**
- * Basic interface for a lambda (used for logging)
- */
+/** Basic interface for a lambda (used for logging) */
 public interface StringValueSupplier {
     /**
      * returns a string value
+     *
      * @return a string
      */
     String getString();
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/paint/PaintBundle.java b/core/java/com/android/internal/widget/remotecompose/core/operations/paint/PaintBundle.java
index 6c8049a..e714947 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/paint/PaintBundle.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/paint/PaintBundle.java
@@ -23,9 +23,7 @@
 
 import java.util.Arrays;
 
-/**
- * Paint Bundle represents a delta of changes to a paint object
- */
+/** Paint Bundle represents a delta of changes to a paint object */
 public class PaintBundle {
     int[] mArray = new int[200];
     int[] mOutArray = null;
@@ -33,6 +31,7 @@
 
     /**
      * Apply changes to a PaintChanges interface
+     *
      * @param paintContext
      * @param p
      */
@@ -46,10 +45,9 @@
             int cmd = mOutArray[i++];
             mask = mask | (1 << (cmd - 1));
             switch (cmd & 0xFFFF) {
-                case TEXT_SIZE: {
+                case TEXT_SIZE:
                     p.setTextSize(Float.intBitsToFloat(mOutArray[i++]));
                     break;
-                }
                 case TYPEFACE:
                     int style = (cmd >> 16);
                     int weight = style & 0x3ff;
@@ -59,99 +57,86 @@
                     p.setTypeFace(font_type, weight, italic);
                     break;
                 case COLOR_ID: // mOutArray should have already decoded it
-                case COLOR: {
+                case COLOR:
                     p.setColor(mOutArray[i++]);
                     break;
-                }
-                case STROKE_WIDTH: {
+                case STROKE_WIDTH:
                     p.setStrokeWidth(Float.intBitsToFloat(mOutArray[i++]));
                     break;
-                }
-                case STROKE_MITER: {
+                case STROKE_MITER:
                     p.setStrokeMiter(Float.intBitsToFloat(mOutArray[i++]));
                     break;
-                }
-                case STROKE_CAP: {
+                case STROKE_CAP:
                     p.setStrokeCap(cmd >> 16);
                     break;
-                }
-                case STYLE: {
+                case STYLE:
                     p.setStyle(cmd >> 16);
                     break;
-                }
-                case SHADER: {
+                case SHADER:
                     p.setShader(mOutArray[i++]);
                     break;
-                }
-                case STROKE_JOIN: {
+                case STROKE_JOIN:
                     p.setStrokeJoin(cmd >> 16);
                     break;
-                }
-                case IMAGE_FILTER_QUALITY: {
+                case IMAGE_FILTER_QUALITY:
                     p.setImageFilterQuality(cmd >> 16);
                     break;
-                }
-                case BLEND_MODE: {
+                case BLEND_MODE:
                     p.setBlendMode(cmd >> 16);
                     break;
-                }
-                case FILTER_BITMAP: {
+                case FILTER_BITMAP:
                     p.setFilterBitmap(!((cmd >> 16) == 0));
                     break;
-                }
-                case GRADIENT: {
+                case GRADIENT:
                     i = callSetGradient(cmd, mOutArray, i, p);
                     break;
-                }
-                case COLOR_FILTER: {
+                case COLOR_FILTER_ID:
+                case COLOR_FILTER:
                     p.setColorFilter(mOutArray[i++], cmd >> 16);
                     break;
-                }
-                case ALPHA: {
+                case ALPHA:
                     p.setAlpha(Float.intBitsToFloat(mOutArray[i++]));
                     break;
-                }
+                case CLEAR_COLOR_FILTER:
+                    p.clear(0x1L << PaintBundle.COLOR_FILTER);
+                    break;
             }
         }
-
-        mask = (~mask) & PaintChanges.VALID_BITS;
-
-        p.clear(mask);
     }
 
-    private String toName(int id) {
-        switch (id) {
-            case TEXT_SIZE:
-                return "TEXT_SIZE";
-            case COLOR:
-                return "COLOR";
-            case STROKE_WIDTH:
-                return "STROKE_WIDTH";
-            case STROKE_MITER:
-                return "STROKE_MITER";
-            case TYPEFACE:
-                return "TYPEFACE";
-            case STROKE_CAP:
-                return "CAP";
-            case STYLE:
-                return "STYLE";
-            case SHADER:
-                return "SHADER";
-            case IMAGE_FILTER_QUALITY:
-                return "IMAGE_FILTER_QUALITY";
-            case BLEND_MODE:
-                return "BLEND_MODE";
-            case FILTER_BITMAP:
-                return "FILTER_BITMAP";
-            case GRADIENT:
-                return "GRADIENT_LINEAR";
-            case ALPHA:
-                return "ALPHA";
-            case COLOR_FILTER:
-                return "COLOR_FILTER";
-        }
-        return "????" + id + "????";
-    }
+    //    private String toName(int id) {
+    //        switch (id) {
+    //            case TEXT_SIZE:
+    //                return "TEXT_SIZE";
+    //            case COLOR:
+    //                return "COLOR";
+    //            case STROKE_WIDTH:
+    //                return "STROKE_WIDTH";
+    //            case STROKE_MITER:
+    //                return "STROKE_MITER";
+    //            case TYPEFACE:
+    //                return "TYPEFACE";
+    //            case STROKE_CAP:
+    //                return "CAP";
+    //            case STYLE:
+    //                return "STYLE";
+    //            case SHADER:
+    //                return "SHADER";
+    //            case IMAGE_FILTER_QUALITY:
+    //                return "IMAGE_FILTER_QUALITY";
+    //            case BLEND_MODE:
+    //                return "BLEND_MODE";
+    //            case FILTER_BITMAP:
+    //                return "FILTER_BITMAP";
+    //            case GRADIENT:
+    //                return "GRADIENT_LINEAR";
+    //            case ALPHA:
+    //                return "ALPHA";
+    //            case COLOR_FILTER:
+    //                return "COLOR_FILTER";
+    //        }
+    //        return "????" + id + "????";
+    //    }
 
     private static String colorInt(int color) {
         String str = "000000000000" + Integer.toHexString(color);
@@ -185,103 +170,189 @@
             int cmd = mArray[i++];
             int type = cmd & 0xFFFF;
             switch (type) {
-
-                case TEXT_SIZE: {
-                    ret.append("    TextSize("
-                            + asFloatStr(mArray[i++]));
-                }
-
-                break;
-                case TYPEFACE: {
+                case TEXT_SIZE:
+                    ret.append("    TextSize(" + asFloatStr(mArray[i++]));
+                    break;
+                case TYPEFACE:
                     int style = (cmd >> 16);
                     int weight = style & 0x3ff;
                     boolean italic = (style >> 10) > 0;
                     int font_type = mArray[i++];
-                    ret.append("    TypeFace(" + (font_type + ", "
-                            + weight + ", " + italic));
-                }
-                break;
-                case COLOR: {
+                    ret.append("    TypeFace(" + (font_type + ", " + weight + ", " + italic));
+                    break;
+                case COLOR:
                     ret.append("    Color(" + colorInt(mArray[i++]));
-                }
-                break;
-                case COLOR_ID: {
+                    break;
+                case COLOR_ID:
                     ret.append("    ColorId([" + mArray[i++] + "]");
-                }
-                break;
-                case STROKE_WIDTH: {
-                    ret.append("    StrokeWidth("
-                            + (asFloatStr(mArray[i++])));
-                }
-                break;
-                case STROKE_MITER: {
-                    ret.append("    StrokeMiter("
-                            + (asFloatStr(mArray[i++])));
-                }
-                break;
-                case STROKE_CAP: {
-                    ret.append("    StrokeCap("
-                            + (cmd >> 16));
-                }
-                break;
-                case STYLE: {
+                    break;
+                case STROKE_WIDTH:
+                    ret.append("    StrokeWidth(" + asFloatStr(mArray[i++]));
+                    break;
+                case STROKE_MITER:
+                    ret.append("    StrokeMiter(" + asFloatStr(mArray[i++]));
+                    break;
+                case STROKE_CAP:
+                    ret.append("    StrokeCap(" + (cmd >> 16));
+                    break;
+                case STYLE:
                     ret.append("    Style(" + (cmd >> 16));
-                }
-                break;
-                case COLOR_FILTER: {
-                    ret.append("    ColorFilter(color="
-                            + colorInt(mArray[i++])
-                            + ", mode=" + blendModeString(cmd >> 16));
-                }
-                break;
-                case SHADER: {
+                    break;
+                case COLOR_FILTER:
+                    ret.append(
+                            "    ColorFilter(color="
+                                    + colorInt(mArray[i++])
+                                    + ", mode="
+                                    + blendModeString(cmd >> 16));
+                    break;
+                case COLOR_FILTER_ID:
+                    ret.append(
+                            "    ColorFilterID(color=["
+                                    + mArray[i++]
+                                    + "], mode="
+                                    + blendModeString(cmd >> 16));
+                    break;
+                case CLEAR_COLOR_FILTER:
+                    ret.append("    clearColorFilter");
+                    break;
+                case SHADER:
                     ret.append("    Shader(" + mArray[i++]);
-                }
-                break;
-                case ALPHA: {
-                    ret.append("    Alpha("
-                            + (asFloatStr(mArray[i++])));
-                }
-                break;
-                case IMAGE_FILTER_QUALITY: {
+                    break;
+                case ALPHA:
+                    ret.append("    Alpha(" + asFloatStr(mArray[i++]));
+                    break;
+                case IMAGE_FILTER_QUALITY:
                     ret.append("    ImageFilterQuality(" + (cmd >> 16));
-                }
-                break;
-                case BLEND_MODE: {
+                    break;
+                case BLEND_MODE:
                     ret.append("    BlendMode(" + blendModeString(cmd >> 16));
-                }
-                break;
-                case FILTER_BITMAP: {
-                    ret.append("    FilterBitmap("
-                            + (!((cmd >> 16) == 0)));
-                }
-                break;
-                case STROKE_JOIN: {
+                    break;
+                case FILTER_BITMAP:
+                    ret.append("    FilterBitmap(" + !(cmd >> 16 == 0));
+                    break;
+                case STROKE_JOIN:
                     ret.append("    StrokeJoin(" + (cmd >> 16));
-                }
-                break;
-                case ANTI_ALIAS: {
+                    break;
+                case ANTI_ALIAS:
                     ret.append("    AntiAlias(" + (cmd >> 16));
-                }
-                break;
-                case GRADIENT: {
+                    break;
+                case GRADIENT:
                     i = callPrintGradient(cmd, mArray, i, ret);
-                }
             }
             ret.append("),\n");
         }
         return ret.toString();
     }
 
+    private void registerFloat(int iv, RemoteContext context, VariableSupport support) {
+        float v = Float.intBitsToFloat(iv);
+        if (Float.isNaN(v)) {
+            context.listensTo(Utils.idFromNan(v), support);
+        }
+    }
+
+    int callRegisterGradient(
+            int cmd, int[] array, int i, RemoteContext context, VariableSupport support) {
+        int ret = i;
+        int type = (cmd >> 16);
+        int control = array[ret++];
+        int len = 0xFF & control; // maximum 256 colors
+        int register = 0xFFFF & (control >> 16);
+        int tileMode = 0;
+        switch (type) {
+            /* see {@link #setLinearGradient} */
+            case LINEAR_GRADIENT:
+                if (len > 0) {
+
+                    for (int j = 0; j < len; j++) {
+                        int color = array[ret++];
+                        if ((register & (1 << j)) != 0) {
+                            context.listensTo(color, support);
+                        }
+                    }
+                }
+                len = array[ret++];
+
+                if (len > 0) {
+
+                    for (int j = 0; j < len; j++) {
+                        registerFloat(array[ret++], context, support);
+                    }
+                }
+
+                //  start x
+                registerFloat(array[ret++], context, support);
+                //  start y
+                registerFloat(array[ret++], context, support);
+                // end x
+                registerFloat(array[ret++], context, support);
+                // end y
+                registerFloat(array[ret++], context, support);
+                tileMode = array[ret++];
+                break;
+            /* see {@link #setRadialGradient} */
+            case RADIAL_GRADIENT:
+                if (len > 0) {
+
+                    for (int j = 0; j < len; j++) {
+                        int color = array[ret++];
+                        if ((register & (1 << j)) != 0) {
+                            context.listensTo(color, support);
+                        }
+                    }
+                }
+                len = array[ret++]; // stops
+                for (int j = 0; j < len; j++) {
+                    registerFloat(array[ret++], context, support);
+                }
+
+                //  center x
+                registerFloat(array[ret++], context, support);
+                //  center y
+                registerFloat(array[ret++], context, support);
+                // radius
+                registerFloat(array[ret++], context, support);
+
+                tileMode = array[ret++]; // tile Mode
+                break;
+            /* see {@link #setSweepGradient} */
+            case SWEEP_GRADIENT:
+                if (len > 0) {
+
+                    for (int j = 0; j < len; j++) {
+                        int color = array[ret++];
+                        if ((register & (1 << j)) != 0) {
+                            context.listensTo(color, support);
+                        }
+                    }
+                }
+
+                len = array[ret++]; // stops
+                for (int j = 0; j < len; j++) {
+                    registerFloat(array[ret++], context, support);
+                }
+                //  center x
+                registerFloat(array[ret++], context, support);
+                //  center y
+                registerFloat(array[ret++], context, support);
+                break;
+            default:
+                System.out.println("error ");
+        }
+
+        return ret;
+    }
+
     int callPrintGradient(int cmd, int[] array, int i, StringBuilder p) {
         int ret = i;
         int type = (cmd >> 16);
+        int tileMode = 0;
+        int len = array[ret++];
+        int[] colors = null;
+        String[] stops = null;
         switch (type) {
-
-            case 0: {
+            case 0:
                 p.append("    LinearGradient(\n");
-                int len = array[ret++];
-                int[] colors = null;
                 if (len > 0) {
                     colors = new int[len];
                     for (int j = 0; j < colors.length; j++) {
@@ -289,7 +360,6 @@
                     }
                 }
                 len = array[ret++];
-                String[] stops = null;
                 if (len > 0) {
                     stops = new String[len];
                     for (int j = 0; j < stops.length; j++) {
@@ -305,24 +375,18 @@
                 p.append("      end = ");
                 p.append("[" + asFloatStr(array[ret++]));
                 p.append(", " + asFloatStr(array[ret++]) + "],\n");
-                int tileMode = array[ret++];
+                tileMode = array[ret++];
                 p.append("      tileMode = " + tileMode + "\n    ");
-            }
-
-            break;
-            case 1: {
+                break;
+            case 1:
                 p.append("    RadialGradient(\n");
-                int len = array[ret++];
-                int[] colors = null;
                 if (len > 0) {
                     colors = new int[len];
                     for (int j = 0; j < colors.length; j++) {
                         colors[j] = array[ret++];
-
                     }
                 }
                 len = array[ret++];
-                String[] stops = null;
                 if (len > 0) {
                     stops = new String[len];
                     for (int j = 0; j < stops.length; j++) {
@@ -337,24 +401,18 @@
                 p.append(", " + asFloatStr(array[ret++]) + "],\n");
                 p.append("      radius =");
                 p.append(" " + asFloatStr(array[ret++]) + ",\n");
-                int tileMode = array[ret++];
+                tileMode = array[ret++];
                 p.append("      tileMode = " + tileMode + "\n    ");
-            }
-
-            break;
-            case 2: {
+                break;
+            case 2:
                 p.append("    SweepGradient(\n");
-                int len = array[ret++];
-                int[] colors = null;
                 if (len > 0) {
                     colors = new int[len];
                     for (int j = 0; j < colors.length; j++) {
                         colors[j] = array[ret++];
-
                     }
                 }
                 len = array[ret++];
-                String[] stops = null;
                 if (len > 0) {
                     stops = new String[len];
                     for (int j = 0; j < stops.length; j++) {
@@ -365,13 +423,10 @@
                 p.append("      stops = " + Arrays.toString(stops) + ",\n");
                 p.append("      center = ");
                 p.append("[" + asFloatStr(array[ret++]));
-                p.append(", "
-                        + asFloatStr(array[ret++]) + "],\n    ");
-            }
-            break;
-            default: {
+                p.append(", " + asFloatStr(array[ret++]) + "],\n    ");
+                break;
+            default:
                 p.append("GRADIENT_??????!!!!");
-            }
         }
 
         return ret;
@@ -381,7 +436,8 @@
         int ret = i;
         int gradientType = (cmd >> 16);
 
-        int len = array[ret++];
+        int len = 0xFF & array[ret++]; // maximum 256 colors
+
         int[] colors = null;
         if (len > 0) {
             colors = new int[len];
@@ -402,33 +458,30 @@
             return ret;
         }
 
-        switch (gradientType) {
+        int tileMode = 0;
+        float centerX = 0f;
+        float centerY = 0f;
 
-            case LINEAR_GRADIENT: {
+        switch (gradientType) {
+            case LINEAR_GRADIENT:
                 float startX = Float.intBitsToFloat(array[ret++]);
                 float startY = Float.intBitsToFloat(array[ret++]);
                 float endX = Float.intBitsToFloat(array[ret++]);
                 float endY = Float.intBitsToFloat(array[ret++]);
-                int tileMode = array[ret++];
-                p.setLinearGradient(colors, stops, startX,
-                        startY, endX, endY, tileMode);
-            }
-
-            break;
-            case RADIAL_GRADIENT: {
-                float centerX = Float.intBitsToFloat(array[ret++]);
-                float centerY = Float.intBitsToFloat(array[ret++]);
+                tileMode = array[ret++];
+                p.setLinearGradient(colors, stops, startX, startY, endX, endY, tileMode);
+                break;
+            case RADIAL_GRADIENT:
+                centerX = Float.intBitsToFloat(array[ret++]);
+                centerY = Float.intBitsToFloat(array[ret++]);
                 float radius = Float.intBitsToFloat(array[ret++]);
-                int tileMode = array[ret++];
-                p.setRadialGradient(colors, stops, centerX, centerY,
-                        radius, tileMode);
-            }
-            break;
-            case SWEEP_GRADIENT: {
-                float centerX = Float.intBitsToFloat(array[ret++]);
-                float centerY = Float.intBitsToFloat(array[ret++]);
+                tileMode = array[ret++];
+                p.setRadialGradient(colors, stops, centerX, centerY, radius, tileMode);
+                break;
+            case SWEEP_GRADIENT:
+                centerX = Float.intBitsToFloat(array[ret++]);
+                centerY = Float.intBitsToFloat(array[ret++]);
                 p.setSweepGradient(colors, stops, centerX, centerY);
-            }
         }
 
         return ret;
@@ -453,9 +506,9 @@
         mPos = len;
     }
 
-    public static final int TEXT_SIZE = 1;  // float
+    public static final int TEXT_SIZE = 1; // float
 
-    public static final int COLOR = 4;  // int
+    public static final int COLOR = 4; // int
     public static final int STROKE_WIDTH = 5; // float
     public static final int STROKE_MITER = 6;
     public static final int STROKE_CAP = 7; //  int
@@ -470,7 +523,9 @@
     public static final int TYPEFACE = 16;
     public static final int FILTER_BITMAP = 17;
     public static final int BLEND_MODE = 18;
-    public static final int COLOR_ID = 19;  // int
+    public static final int COLOR_ID = 19;
+    public static final int COLOR_FILTER_ID = 20;
+    public static final int CLEAR_COLOR_FILTER = 21;
 
     public static final int BLEND_MODE_CLEAR = 0;
     public static final int BLEND_MODE_SRC = 1;
@@ -524,27 +579,28 @@
     /**
      * sets a shader that draws a linear gradient along a line.
      *
-     * @param startX   The x-coordinate for the start of the gradient line
-     * @param startY   The y-coordinate for the start of the gradient line
-     * @param endX     The x-coordinate for the end of the gradient line
-     * @param endY     The y-coordinate for the end of the gradient line
-     * @param colors   The sRGB colors to be distributed along the gradient line
-     * @param stops    May be null. The relative positions [0..1] of
-     *                 each corresponding color in the colors array. If this is null,
-     *                 the colors are distributed evenly along the gradient line.
+     * @param startX The x-coordinate for the start of the gradient line
+     * @param startY The y-coordinate for the start of the gradient line
+     * @param endX The x-coordinate for the end of the gradient line
+     * @param endY The y-coordinate for the end of the gradient line
+     * @param colors The sRGB colors to be distributed along the gradient line
+     * @param stops May be null. The relative positions [0..1] of each corresponding color in the
+     *     colors array. If this is null, the colors are distributed evenly along the gradient line.
      * @param tileMode The Shader tiling mode
      */
-    public void setLinearGradient(int[] colors,
-                                  float[] stops,
-                                  float startX,
-                                  float startY,
-                                  float endX,
-                                  float endY,
-                                  int tileMode) {
-        int startPos = mPos;
+    public void setLinearGradient(
+            int[] colors,
+            int idMask,
+            float[] stops,
+            float startX,
+            float startY,
+            float endX,
+            float endY,
+            int tileMode) {
+        //        int startPos = mPos;
         int len;
         mArray[mPos++] = GRADIENT | (LINEAR_GRADIENT << 16);
-        mArray[mPos++] = len = (colors == null) ? 0 : colors.length;
+        mArray[mPos++] = (idMask << 16) | (len = (colors == null) ? 0 : colors.length);
         for (int i = 0; i < len; i++) {
             mArray[mPos++] = colors[i];
         }
@@ -565,20 +621,18 @@
      *
      * @param centerX The x-coordinate of the center
      * @param centerY The y-coordinate of the center
-     * @param colors  The sRGB colors to be distributed around the center.
-     *                There must be at least 2 colors in the array.
-     * @param stops   May be NULL. The relative position of
-     *                each corresponding color in the colors array, beginning
-     *                with 0 and ending with 1.0. If the values are not
-     *                monotonic, the drawing may produce unexpected results.
-     *                If positions is NULL, then the colors are automatically
-     *                spaced evenly.
+     * @param colors The sRGB colors to be distributed around the center. There must be at least 2
+     *     colors in the array.
+     * @param stops May be NULL. The relative position of each corresponding color in the colors
+     *     array, beginning with 0 and ending with 1.0. If the values are not monotonic, the drawing
+     *     may produce unexpected results. If positions is NULL, then the colors are automatically
+     *     spaced evenly.
      */
-    public void setSweepGradient(int[] colors, float[] stops, float centerX, float centerY) {
-        int startPos = mPos;
+    public void setSweepGradient(
+            int[] colors, int idMask, float[] stops, float centerX, float centerY) {
         int len;
         mArray[mPos++] = GRADIENT | (SWEEP_GRADIENT << 16);
-        mArray[mPos++] = len = (colors == null) ? 0 : colors.length;
+        mArray[mPos++] = (idMask << 16) | (len = (colors == null) ? 0 : colors.length);
         for (int i = 0; i < len; i++) {
             mArray[mPos++] = colors[i];
         }
@@ -594,29 +648,28 @@
     /**
      * Sets a shader that draws a radial gradient given the center and radius.
      *
-     * @param centerX  The x-coordinate of the center of the radius
-     * @param centerY  The y-coordinate of the center of the radius
-     * @param radius   Must be positive. The radius of the gradient.
-     * @param colors   The sRGB colors distributed between the center and edge
-     * @param stops    May be <code>null</code>.
-     *                 Valid values are between <code>0.0f</code> and
-     *                 <code>1.0f</code>. The relative position of each
-     *                 corresponding color in
-     *                 the colors array. If <code>null</code>, colors are
-     *                 distributed evenly
-     *                 between the center and edge of the circle.
+     * @param centerX The x-coordinate of the center of the radius
+     * @param centerY The y-coordinate of the center of the radius
+     * @param radius Must be positive. The radius of the gradient.
+     * @param colors The sRGB colors distributed between the center and edge
+     * @param stops May be <code>null</code>. Valid values are between <code>0.0f</code> and <code>
+     *      1.0f</code>. The relative position of each corresponding color in the colors array. If
+     *     <code>null</code>, colors are distributed evenly between the center and edge of the
+     *     circle.
      * @param tileMode The Shader tiling mode
      */
-    public void setRadialGradient(int[] colors,
-                                  float[] stops,
-                                  float centerX,
-                                  float centerY,
-                                  float radius,
-                                  int tileMode) {
-        int startPos = mPos;
+    public void setRadialGradient(
+            int[] colors,
+            int idMask,
+            float[] stops,
+            float centerX,
+            float centerY,
+            float radius,
+            int tileMode) {
+        //        int startPos = mPos;
         int len;
         mArray[mPos++] = GRADIENT | (RADIAL_GRADIENT << 16);
-        mArray[mPos++] = len = (colors == null) ? 0 : colors.length;
+        mArray[mPos++] = (idMask << 16) | (len = (colors == null) ? 0 : colors.length);
         for (int i = 0; i < len; i++) {
             mArray[mPos++] = colors[i];
         }
@@ -629,14 +682,13 @@
         mArray[mPos++] = Float.floatToRawIntBits(centerY);
         mArray[mPos++] = Float.floatToRawIntBits(radius);
         mArray[mPos++] = tileMode;
-
     }
 
     /**
      * Create a color filter that uses the specified color and Porter-Duff mode.
      *
      * @param color The ARGB source color used with the Porter-Duff mode
-     * @param mode  The porter-duff mode that is applied
+     * @param mode The porter-duff mode that is applied
      */
     public void setColorFilter(int color, int mode) {
         mArray[mPos] = COLOR_FILTER | (mode << 16);
@@ -645,12 +697,29 @@
     }
 
     /**
+     * Create a color filter that uses the specified color and Porter-Duff mode.
+     *
+     * @param color The id source color used with the Porter-Duff mode
+     * @param mode The porter-duff mode that is applied
+     */
+    public void setColorFilterId(int color, int mode) {
+        mArray[mPos] = COLOR_FILTER_ID | (mode << 16);
+        mPos++;
+        mArray[mPos++] = color;
+    }
+
+    /** This sets the color filter to null */
+    public void clearColorFilter() {
+        mArray[mPos] = CLEAR_COLOR_FILTER;
+        mPos++;
+    }
+
+    /**
      * Set the paint's text size. This value must be > 0
      *
      * @param size set the paint's text size in pixel units.
      */
     public void setTextSize(float size) {
-        int p = mPos;
         mArray[mPos] = TEXT_SIZE;
         mPos++;
         mArray[mPos] = Float.floatToRawIntBits(size);
@@ -659,22 +728,21 @@
 
     /**
      * @param fontType 0 = default 1 = sans serif 2 = serif 3 = monospace
-     * @param weight   100-1000
-     * @param italic   tur
+     * @param weight 100-1000
+     * @param italic tur
      */
     public void setTextStyle(int fontType, int weight, boolean italic) {
-        int style = (weight & 0x3FF) | (italic ? 2048 : 0);  // pack the weight and italic
+        int style = (weight & 0x3FF) | (italic ? 2048 : 0); // pack the weight and italic
         mArray[mPos++] = TYPEFACE | (style << 16);
         mArray[mPos++] = fontType;
     }
 
     /**
-     * Set the width for stroking.
-     * Pass 0 to stroke in hairline mode.
-     * Hairlines always draws a single pixel independent of the canvas's matrix.
+     * Set the width for stroking. Pass 0 to stroke in hairline mode. Hairlines always draws a
+     * single pixel independent of the canvas's matrix.
      *
-     * @param width set the paint's stroke width, used whenever the paint's
-     *              style is Stroke or StrokeAndFill.
+     * @param width set the paint's stroke width, used whenever the paint's style is Stroke or
+     *     StrokeAndFill.
      */
     public void setStrokeWidth(float width) {
         mArray[mPos] = STROKE_WIDTH;
@@ -685,6 +753,7 @@
 
     /**
      * Set the Color based on Color
+     *
      * @param color
      */
     public void setColor(int color) {
@@ -696,6 +765,7 @@
 
     /**
      * Set the color based the R,G,B,A values
+     *
      * @param r red (0 to 255)
      * @param g green (0 to 255)
      * @param b blue (0 to 255)
@@ -708,6 +778,7 @@
 
     /**
      * Set the color based the R,G,B,A values
+     *
      * @param r red (0.0 to 1.0)
      * @param g green (0.0 to 1.0)
      * @param b blue (0.0 to 1.0)
@@ -719,6 +790,7 @@
 
     /**
      * Set the Color based on ID
+     *
      * @param color
      */
     public void setColorId(int color) {
@@ -728,12 +800,11 @@
         mPos++;
     }
 
-
     /**
      * Set the paint's Cap.
      *
-     * @param cap set the paint's line cap style, used whenever the paint's
-     *            style is Stroke or StrokeAndFill.
+     * @param cap set the paint's line cap style, used whenever the paint's style is Stroke or
+     *     StrokeAndFill.
      */
     public void setStrokeCap(int cap) {
         mArray[mPos] = STROKE_CAP | (cap << 16);
@@ -742,6 +813,7 @@
 
     /**
      * Set the style STROKE and/or FILL
+     *
      * @param style
      */
     public void setStyle(int style) {
@@ -751,6 +823,7 @@
 
     /**
      * Set the shader id to use
+     *
      * @param shaderId
      */
     public void setShader(int shaderId) {
@@ -760,9 +833,7 @@
         mPos++;
     }
 
-    /**
-     * Set the Alpha value
-     */
+    /** Set the Alpha value */
     public void setAlpha(float alpha) {
         mArray[mPos] = ALPHA;
         mPos++;
@@ -771,11 +842,11 @@
     }
 
     /**
-     * Set the paint's stroke miter value. This is used to control the behavior
-     * of miter joins when the joins angle is sharp. This value must be >= 0.
+     * Set the paint's stroke miter value. This is used to control the behavior of miter joins when
+     * the joins angle is sharp. This value must be >= 0.
      *
-     * @param miter set the miter limit on the paint, used whenever the paint's
-     *              style is Stroke or StrokeAndFill.
+     * @param miter set the miter limit on the paint, used whenever the paint's style is Stroke or
+     *     StrokeAndFill.
      */
     public void setStrokeMiter(float miter) {
         mArray[mPos] = STROKE_MITER;
@@ -787,8 +858,7 @@
     /**
      * Set the paint's Join.
      *
-     * @param join set the paint's Join, used whenever the paint's style is
-     *             Stroke or StrokeAndFill.
+     * @param join set the paint's Join, used whenever the paint's style is Stroke or StrokeAndFill.
      */
     public void setStrokeJoin(int join) {
         mArray[mPos] = STROKE_JOIN | (join << 16);
@@ -801,10 +871,8 @@
     }
 
     /**
-     * Set or clear the blend mode. A blend mode defines how source pixels
-     * (generated by a drawing command) are composited with the
-     * destination pixels
-     * (content of the render target).
+     * Set or clear the blend mode. A blend mode defines how source pixels (generated by a drawing
+     * command) are composited with the destination pixels (content of the render target).
      *
      * @param blendmode The blend mode to be installed in the paint
      */
@@ -814,15 +882,14 @@
     }
 
     /**
-     * Helper for setFlags(), setting or clearing the ANTI_ALIAS_FLAG bit
-     * AntiAliasing smooths out the edges of what is being drawn, but is has
-     * no impact on the interior of the shape. See setDither() and
-     * setFilterBitmap() to affect how colors are treated.
+     * Helper for setFlags(), setting or clearing the ANTI_ALIAS_FLAG bit AntiAliasing smooths out
+     * the edges of what is being drawn, but is has no impact on the interior of the shape. See
+     * setDither() and setFilterBitmap() to affect how colors are treated.
      *
      * @param aa true to set the antialias bit in the flags, false to clear it
      */
     public void setAntiAlias(boolean aa) {
-        mArray[mPos] = ANTI_ALIAS | (((aa) ? 1 : 0) << 16);
+        mArray[mPos] = ANTI_ALIAS | (aa ? 1 : 0) << 16;
         mPos++;
     }
 
@@ -903,6 +970,7 @@
 
     /**
      * Check all the floats for Nan(id) floats and call listenTo
+     *
      * @param context
      * @param support
      */
@@ -921,6 +989,7 @@
                         context.listensTo(Utils.idFromNan(v), support);
                     }
                     break;
+                case COLOR_FILTER_ID:
                 case COLOR_ID:
                     context.listensTo(mArray[i++], support);
                     break;
@@ -940,16 +1009,15 @@
                 case ANTI_ALIAS:
                     break;
 
-                case GRADIENT: {
-                    // TODO gradients should be handled correctly
-                    i = callPrintGradient(cmd, mArray, i, new StringBuilder());
-                }
+                case GRADIENT:
+                    i = callRegisterGradient(cmd, mArray, i, context, support);
             }
         }
     }
 
     /**
      * Update variables if any are float ids
+     *
      * @param context
      */
     public void updateVariables(RemoteContext context) {
@@ -970,6 +1038,7 @@
                     mOutArray[i] = fixFloatVar(mArray[i], context);
                     i++;
                     break;
+                case COLOR_FILTER_ID:
                 case COLOR_ID:
                     mOutArray[i] = fixColor(mArray[i], context);
                     i++;
@@ -987,12 +1056,12 @@
                 case IMAGE_FILTER_QUALITY:
                 case BLEND_MODE:
                 case ANTI_ALIAS:
+                case CLEAR_COLOR_FILTER:
                     break;
 
-                case GRADIENT: {
+                case GRADIENT:
                     // TODO gradients should be handled correctly
                     i = updateFloatsInGradient(cmd, mOutArray, mArray, i, context);
-                }
             }
         }
     }
@@ -1011,21 +1080,25 @@
         return n;
     }
 
-    int updateFloatsInGradient(int cmd, int[] out, int[] array,
-                               int i,
-                               RemoteContext context) {
+    int updateFloatsInGradient(int cmd, int[] out, int[] array, int i, RemoteContext context) {
         int ret = i;
         int type = (cmd >> 16);
+        int control = array[ret++];
+        int len = 0xFF & control; // maximum 256 colors
+        int register = 0xFFFF & (control >> 16);
         switch (type) {
-            case 0: {
-                int len = array[ret++];
+            case LINEAR_GRADIENT:
                 if (len > 0) {
+
                     for (int j = 0; j < len; j++) {
+                        int color = array[ret];
+                        if ((register & (1 << j)) != 0) {
+                            out[ret] = fixColor(color, context);
+                        }
                         ret++;
                     }
                 }
                 len = array[ret++];
-
                 if (len > 0) {
                     for (int j = 0; j < len; j++) {
                         out[ret] = fixFloatVar(array[ret], context);
@@ -1044,14 +1117,16 @@
                 out[ret] = fixFloatVar(array[ret], context);
                 ret++;
                 ret++; // tileMode
-            }
-
-            break;
-            case 1: {
+                break;
+            case RADIAL_GRADIENT:
                 //   RadialGradient
-                int len = array[ret++];
                 if (len > 0) {
+
                     for (int j = 0; j < len; j++) {
+                        int color = array[ret];
+                        if ((register & (1 << j)) != 0) {
+                            out[ret] = fixColor(color, context);
+                        }
                         ret++;
                     }
                 }
@@ -1063,7 +1138,6 @@
                     }
                 }
 
-
                 //    center
                 out[ret] = fixFloatVar(array[ret], context);
                 ret++;
@@ -1073,19 +1147,17 @@
                 out[ret] = fixFloatVar(array[ret], context);
                 ret++;
                 ret++; // tileMode
-
-            }
-
-            break;
-            case 2: {
+                break;
+            case SWEEP_GRADIENT:
                 //   SweepGradient
-                int len = array[ret++];
-                int[] colors = null;
                 if (len > 0) {
-                    colors = new int[len];
-                    for (int j = 0; j < colors.length; j++) {
-                        colors[j] = array[ret++];
 
+                    for (int j = 0; j < len; j++) {
+                        int color = array[ret];
+                        if ((register & (1 << j)) != 0) {
+                            out[ret] = fixColor(color, context);
+                        }
+                        ret++;
                     }
                 }
                 len = array[ret++];
@@ -1103,14 +1175,12 @@
                 ret++;
                 out[ret] = fixFloatVar(array[ret], context);
                 ret++;
-            }
-            break;
-            default: {
+
+                break;
+            default:
                 System.err.println("gradient type unknown");
-            }
         }
 
         return ret;
     }
-
-}
\ No newline at end of file
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/paint/PaintChangeAdapter.java b/core/java/com/android/internal/widget/remotecompose/core/operations/paint/PaintChangeAdapter.java
index 28fe63a..e2402be 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/paint/PaintChangeAdapter.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/paint/PaintChangeAdapter.java
@@ -18,112 +18,73 @@
 public class PaintChangeAdapter implements PaintChanges {
 
     @Override
-    public void setTextSize(float size) {
-
-    }
+    public void setTextSize(float size) {}
 
     @Override
-    public void setTypeFace(int fontType, int weight, boolean italic) {
-
-    }
+    public void setTypeFace(int fontType, int weight, boolean italic) {}
 
     @Override
-    public void setStrokeWidth(float width) {
-
-    }
+    public void setStrokeWidth(float width) {}
 
     @Override
-    public void setColor(int color) {
-
-    }
+    public void setColor(int color) {}
 
     @Override
-    public void setStrokeCap(int cap) {
-
-    }
+    public void setStrokeCap(int cap) {}
 
     @Override
-    public void setStyle(int style) {
-
-    }
+    public void setStyle(int style) {}
 
     @Override
-    public void setShader(int shader) {
-
-    }
+    public void setShader(int shader) {}
 
     @Override
-    public void setImageFilterQuality(int quality) {
-
-    }
+    public void setImageFilterQuality(int quality) {}
 
     @Override
-    public void setAlpha(float a) {
-
-    }
+    public void setAlpha(float a) {}
 
     @Override
-    public void setStrokeMiter(float miter) {
-
-    }
+    public void setStrokeMiter(float miter) {}
 
     @Override
-    public void setStrokeJoin(int join) {
-
-    }
+    public void setStrokeJoin(int join) {}
 
     @Override
-    public void setFilterBitmap(boolean filter) {
-
-    }
+    public void setFilterBitmap(boolean filter) {}
 
     @Override
-    public void setBlendMode(int blendmode) {
-
-    }
+    public void setBlendMode(int blendmode) {}
 
     @Override
-    public void setAntiAlias(boolean aa) {
-
-    }
+    public void setAntiAlias(boolean aa) {}
 
     @Override
-    public void clear(long mask) {
-
-    }
+    public void clear(long mask) {}
 
     @Override
-    public void setLinearGradient(int[] colorsArray,
-                                  float[] stopsArray,
-                                  float startX,
-                                  float startY,
-                                  float endX,
-                                  float endY,
-                                  int tileMode) {
-
-    }
+    public void setLinearGradient(
+            int[] colorsArray,
+            float[] stopsArray,
+            float startX,
+            float startY,
+            float endX,
+            float endY,
+            int tileMode) {}
 
     @Override
-    public void setRadialGradient(int[] colorsArray,
-                                  float[] stopsArray,
-                                  float centerX,
-                                  float centerY,
-                                  float radius,
-                                  int tileMode) {
-
-    }
+    public void setRadialGradient(
+            int[] colorsArray,
+            float[] stopsArray,
+            float centerX,
+            float centerY,
+            float radius,
+            int tileMode) {}
 
     @Override
-    public void setSweepGradient(int[] colorsArray,
-                                 float[] stopsArray,
-                                 float centerX,
-                                 float centerY) {
-
-    }
+    public void setSweepGradient(
+            int[] colorsArray, float[] stopsArray, float centerX, float centerY) {}
 
     @Override
-    public void setColorFilter(int color, int mode) {
-
-    }
-
+    public void setColorFilter(int color, int mode) {}
 }
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/paint/PaintChanges.java b/core/java/com/android/internal/widget/remotecompose/core/operations/paint/PaintChanges.java
index d5dc388..486d763 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/paint/PaintChanges.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/paint/PaintChanges.java
@@ -15,10 +15,7 @@
  */
 package com.android.internal.widget.remotecompose.core.operations.paint;
 
-/**
- * Interface to a paint object
- * For more details see Android Paint
- */
+/** Interface to a paint object For more details see Android Paint */
 public interface PaintChanges {
 
     // MASK to be set/cleared
@@ -30,8 +27,7 @@
     int CLEAR_CAP = 1 << (PaintBundle.STROKE_CAP - 1);
     int CLEAR_STYLE = 1 << (PaintBundle.STYLE - 1);
     int CLEAR_SHADER = 1 << (PaintBundle.SHADER - 1);
-    int CLEAR_IMAGE_FILTER_QUALITY =
-            1 << (PaintBundle.IMAGE_FILTER_QUALITY - 1);
+    int CLEAR_IMAGE_FILTER_QUALITY = 1 << (PaintBundle.IMAGE_FILTER_QUALITY - 1);
     int CLEAR_RADIENT = 1 << (PaintBundle.GRADIENT - 1);
     int CLEAR_ALPHA = 1 << (PaintBundle.ALPHA - 1);
     int CLEAR_COLOR_FILTER = 1 << (PaintBundle.COLOR_FILTER - 1);
@@ -39,91 +35,105 @@
 
     /**
      * Set the size of text
+     *
      * @param size
      */
     void setTextSize(float size);
 
     /**
      * Set the width of lines
+     *
      * @param width
      */
     void setStrokeWidth(float width);
 
     /**
      * Set the color to use
+     *
      * @param color
      */
     void setColor(int color);
 
     /**
      * Set the Stroke Cap
+     *
      * @param cap
      */
     void setStrokeCap(int cap);
 
     /**
      * Set the Stroke style FILL and/or STROKE
+     *
      * @param style
      */
     void setStyle(int style);
 
     /**
      * Set the id of the shader to use
+     *
      * @param shader
      */
     void setShader(int shader);
 
     /**
      * Set the way image is interpolated
+     *
      * @param quality
      */
     void setImageFilterQuality(int quality);
 
     /**
      * Set the alpha to draw under
+     *
      * @param a
      */
     void setAlpha(float a);
 
     /**
      * Set the Stroke Miter
+     *
      * @param miter
      */
     void setStrokeMiter(float miter);
 
     /**
      * Set the Stroke Join
+     *
      * @param join
      */
     void setStrokeJoin(int join);
 
     /**
      * Should bitmaps be interpolated
+     *
      * @param filter
      */
     void setFilterBitmap(boolean filter);
 
     /**
      * Set the blend mode can be porterduff + others
+     *
      * @param mode
      */
     void setBlendMode(int mode);
 
     /**
-     * Set the AntiAlias. Typically true
-     * Set to off when you need pixilated look (e.g. QR codes)
+     * Set the AntiAlias. Typically true Set to off when you need pixilated look (e.g. QR codes)
+     *
      * @param aa
      */
     void setAntiAlias(boolean aa);
 
     /**
      * Clear some sub set of the settings
+     *
      * @param mask
      */
     void clear(long mask);
 
     /**
      * Set a linear gradient fill
+     *
      * @param colorsArray
      * @param stopsArray
      * @param startX
@@ -139,11 +149,11 @@
             float startY,
             float endX,
             float endY,
-            int tileMode
-    );
+            int tileMode);
 
     /**
      * Set a radial gradient fill
+     *
      * @param colorsArray
      * @param stopsArray
      * @param centerX
@@ -157,36 +167,32 @@
             float centerX,
             float centerY,
             float radius,
-            int tileMode
-    );
+            int tileMode);
 
     /**
      * Set a sweep gradient fill
+     *
      * @param colorsArray
      * @param stopsArray
      * @param centerX
      * @param centerY
      */
-    void setSweepGradient(
-            int[] colorsArray,
-            float[] stopsArray,
-            float centerX,
-            float centerY
-    );
+    void setSweepGradient(int[] colorsArray, float[] stopsArray, float centerX, float centerY);
 
     /**
      * Set Color filter mod
+     *
      * @param color
      * @param mode
      */
     void setColorFilter(int color, int mode);
 
     /**
-     * Set TypeFace 0,1,2
-     * TODO above should point to a string to be decoded
+     * Set TypeFace 0,1,2 TODO above should point to a string to be decoded
+     *
      * @param fontType
      * @param weight
      * @param italic
      */
     void setTypeFace(int fontType, int weight, boolean italic);
-}
\ No newline at end of file
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/paint/Painter.java b/core/java/com/android/internal/widget/remotecompose/core/operations/paint/Painter.java
index ada3757..9a3cd54 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/paint/Painter.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/paint/Painter.java
@@ -15,16 +15,11 @@
  */
 package com.android.internal.widget.remotecompose.core.operations.paint;
 
-
-/**
- * Provides a Builder pattern for a PaintBundle
- */
+/** Provides a Builder pattern for a PaintBundle */
 class Painter {
     PaintBundle mPaint;
 
-    /**
-     * Write the paint to the buffer
-     */
+    /** Write the paint to the buffer */
     public PaintBundle commit() {
         return mPaint;
     }
@@ -47,8 +42,7 @@
     /**
      * Set the paint's Join.
      *
-     * @param join set the paint's Join, used whenever the paint's style is
-     *             Stroke or StrokeAndFill.
+     * @param join set the paint's Join, used whenever the paint's style is Stroke or StrokeAndFill.
      */
     public Painter setStrokeJoin(int join) {
         mPaint.setStrokeJoin(join);
@@ -56,12 +50,11 @@
     }
 
     /**
-     * Set the width for stroking. Pass 0 to stroke in hairline mode.
-     * Hairlines always draws a single
-     * pixel independent of the canvas's matrix.
+     * Set the width for stroking. Pass 0 to stroke in hairline mode. Hairlines always draws a
+     * single pixel independent of the canvas's matrix.
      *
-     * @param width set the paint's stroke width, used whenever the paint's
-     *             style is Stroke or StrokeAndFill.
+     * @param width set the paint's stroke width, used whenever the paint's style is Stroke or
+     *     StrokeAndFill.
      */
     public Painter setStrokeWidth(float width) {
         mPaint.setStrokeWidth(width);
@@ -69,8 +62,8 @@
     }
 
     /**
-     * Set the paint's style, used for controlling how primitives' geometries
-     * are interpreted (except for drawBitmap, which always assumes Fill).
+     * Set the paint's style, used for controlling how primitives' geometries are interpreted
+     * (except for drawBitmap, which always assumes Fill).
      *
      * @param style The new style to set in the paint
      */
@@ -82,8 +75,8 @@
     /**
      * Set the paint's Cap.
      *
-     * @param cap set the paint's line cap style, used whenever the paint's
-     *           style is Stroke or StrokeAndFill.
+     * @param cap set the paint's line cap style, used whenever the paint's style is Stroke or
+     *     StrokeAndFill.
      */
     public Painter setStrokeCap(int cap) {
         mPaint.setStrokeCap(cap);
@@ -91,11 +84,11 @@
     }
 
     /**
-     * Set the paint's stroke miter value. This is used to control the behavior
-     * of miter joins when the joins angle is sharp. This value must be >= 0.
+     * Set the paint's stroke miter value. This is used to control the behavior of miter joins when
+     * the joins angle is sharp. This value must be >= 0.
      *
-     * @param miter set the miter limit on the paint, used whenever the paint's
-     *             style is Stroke or StrokeAndFill.
+     * @param miter set the miter limit on the paint, used whenever the paint's style is Stroke or
+     *     StrokeAndFill.
      */
     public Painter setStrokeMiter(float miter) {
         mPaint.setStrokeMiter(miter);
@@ -103,9 +96,8 @@
     }
 
     /**
-     * Helper to setColor(), that only assigns the color's alpha value,
-     * leaving its r,g,b values unchanged. Results are undefined if the alpha
-     * value is outside of the range [0..1.0]
+     * Helper to setColor(), that only assigns the color's alpha value, leaving its r,g,b values
+     * unchanged. Results are undefined if the alpha value is outside of the range [0..1.0]
      *
      * @param alpha set the alpha component [0..1.0] of the paint's color.
      */
@@ -117,9 +109,8 @@
     /**
      * Create a color filter that uses the specified color and Porter-Duff mode.
      *
-     * @param color The ARGB source color used with the specified Porter-Duff
-     *             mode
-     * @param mode  The porter-duff mode that is applied
+     * @param color The ARGB source color used with the specified Porter-Duff mode
+     * @param mode The porter-duff mode that is applied
      */
     public Painter setPorterDuffColorFilter(int color, int mode) {
         mPaint.setColorFilter(color, mode);
@@ -129,17 +120,15 @@
     /**
      * sets a shader that draws a linear gradient along a line.
      *
-     * @param startX    The x-coordinate for the start of the gradient line
-     * @param startY    The y-coordinate for the start of the gradient line
-     * @param endX      The x-coordinate for the end of the gradient line
-     * @param endY      The y-coordinate for the end of the gradient line
-     * @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 colors are distributed evenly along the gradient
-     *                 line.
-     * @param tileMode  The Shader tiling mode
+     * @param startX The x-coordinate for the start of the gradient line
+     * @param startY The y-coordinate for the start of the gradient line
+     * @param endX The x-coordinate for the end of the gradient line
+     * @param endY The y-coordinate for the end of the gradient line
+     * @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 colors are distributed evenly along the gradient
+     *     line.
+     * @param tileMode The Shader tiling mode
      */
     public Painter setLinearGradient(
             float startX,
@@ -148,29 +137,23 @@
             float endY,
             int[] colors,
             float[] positions,
-            int tileMode
-    ) {
-        mPaint.setLinearGradient(colors, positions, startX,
-                startY, endX, endY, tileMode);
+            int tileMode) {
+        mPaint.setLinearGradient(colors, 0, positions, startX, startY, endX, endY, tileMode);
         return this;
     }
 
     /**
      * Sets a shader that draws a radial gradient given the center and radius.
      *
-     * @param centerX   The x-coordinate of the center of the radius
-     * @param centerY   The y-coordinate of the center of the radius
-     * @param radius    Must be positive. The radius of the circle for this
-     *                  gradient.
-     * @param colors    The sRGB colors to be distributed between the center
-     *                  and edge of the circle
-     * @param positions May be <code>null</code>. Valid values are between
-     *                  <code>0.0f</code> and
-     *                  <code>1.0f</code>. The relative position of each
-     *                  corresponding color in the colors array. If
-     *                  <code>null</code>, colors are distributed evenly
-     *                  between the center and edge of the circle.
-     * @param tileMode  The Shader tiling mode
+     * @param centerX The x-coordinate of the center of the radius
+     * @param centerY The y-coordinate of the center of the radius
+     * @param radius Must be positive. The radius of the circle for this gradient.
+     * @param colors The sRGB colors to be distributed between the center and edge of the circle
+     * @param positions May be <code>null</code>. Valid values are between <code>0.0f</code> and
+     *     <code>1.0f</code>. The relative position of each corresponding color in the colors array.
+     *     If <code>null</code>, colors are distributed evenly between the center and edge of the
+     *     circle.
+     * @param tileMode The Shader tiling mode
      */
     public Painter setRadialGradient(
             float centerX,
@@ -178,33 +161,25 @@
             float radius,
             int[] colors,
             float[] positions,
-            int tileMode
-    ) {
-        mPaint.setRadialGradient(colors, positions, centerX,
-                centerY, radius, tileMode);
+            int tileMode) {
+        mPaint.setRadialGradient(colors, 0, positions, centerX, centerY, radius, tileMode);
         return this;
     }
 
     /**
      * Set a shader that draws a sweep gradient around a center point.
      *
-     * @param centerX   The x-coordinate of the center
-     * @param centerY   The y-coordinate of the center
-     * @param colors    The sRGB colors to be distributed between around the
-     *                  center. There must be at least 2 colors in the array.
-     * @param positions May be NULL. The relative position of each corresponding
-     *                 color in the colors array, beginning with 0 and ending
-     *                 with 1.0. If the values are not monotonic, the drawing
-     *                  may produce unexpected results. If positions is NULL,
-     *                  then the colors are automatically spaced evenly.
+     * @param centerX The x-coordinate of the center
+     * @param centerY The y-coordinate of the center
+     * @param colors The sRGB colors to be distributed between around the center. There must be at
+     *     least 2 colors in the array.
+     * @param positions May be NULL. The relative position of each corresponding color in the colors
+     *     array, beginning with 0 and ending with 1.0. If the values are not monotonic, the drawing
+     *     may produce unexpected results. If positions is NULL, then the colors are automatically
+     *     spaced evenly.
      */
-    public Painter setSweepGradient(
-            float centerX,
-            float centerY,
-            int[] colors,
-            float[] positions
-    ) {
-        mPaint.setSweepGradient(colors, positions, centerX, centerY);
+    public Painter setSweepGradient(float centerX, float centerY, int[] colors, float[] positions) {
+        mPaint.setSweepGradient(colors, 0, positions, centerX, centerY);
         return this;
     }
 
@@ -219,10 +194,11 @@
     }
 
     /**
-     * sets a typeface object that best matches the specified existing
-     * typeface and the specified weight and italic style
+     * sets a typeface object that best matches the specified existing typeface and the specified
+     * weight and italic style
      *
-     * <p>Below are numerical values and corresponding common weight names.</p>
+     * <p>Below are numerical values and corresponding common weight names.
+     *
      * <table> <thead>
      * <tr><th>Value</th><th>Common weight name</th></tr> </thead> <tbody>
      * <tr><td>100</td><td>Thin</td></tr>
@@ -236,25 +212,21 @@
      * <tr><td>900</td><td>Black</td></tr> </tbody> </table>
      *
      * @param fontType 0 = default 1 = sans serif 2 = serif 3 = monospace
-     * @param weight   The desired weight to be drawn.
-     * @param italic   {@code true} if italic style is desired to be drawn.
-     *                            Otherwise, {@code false}
+     * @param weight The desired weight to be drawn.
+     * @param italic {@code true} if italic style is desired to be drawn. Otherwise, {@code false}
      */
     public Painter setTypeface(int fontType, int weight, boolean italic) {
         mPaint.setTextStyle(fontType, weight, italic);
         return this;
     }
 
-
     public Painter setFilterBitmap(boolean filter) {
         mPaint.setFilterBitmap(filter);
         return this;
     }
 
-
     public Painter setShader(int id) {
         mPaint.setShader(id);
         return this;
     }
-
 }
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/AnimatedFloatExpression.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/AnimatedFloatExpression.java
index 5b295eb..1d673c4 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/AnimatedFloatExpression.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/AnimatedFloatExpression.java
@@ -15,9 +15,6 @@
  */
 package com.android.internal.widget.remotecompose.core.operations.utilities;
 
-import static com.android.internal.widget.remotecompose.core.operations.utilities.NanMap.ID_REGION_ARRAY;
-import static com.android.internal.widget.remotecompose.core.operations.utilities.NanMap.ID_REGION_MASK;
-
 /**
  * high performance floating point expression evaluator used in animation
  */
@@ -59,7 +56,6 @@
     public static final float RAD = asNan(OFFSET + 30);
     public static final float CEIL = asNan(OFFSET + 31);
 
-
     // Array ops
     public static final float A_DEREF = asNan(OFFSET + 32);
     public static final float A_MAX = asNan(OFFSET + 33);
@@ -69,14 +65,13 @@
     public static final float A_LEN = asNan(OFFSET + 37);
     public static final int LAST_OP = OFFSET + 37;
 
-
     public static final float VAR1 = asNan(OFFSET + 38);
     public static final float VAR2 = asNan(OFFSET + 39);
 
     // TODO CLAMP, CBRT, DEG, RAD, EXPM1, CEIL, FLOOR
-    private static final float FP_PI = (float) Math.PI;
-    private static final float FP_TO_RAD = 57.29577951f; // 180/PI
-    private static final float FP_TO_DEG = 0.01745329252f; // 180/PI
+    //    private static final float FP_PI = (float) Math.PI;
+    private static final float FP_TO_RAD = 57.29578f; // 180/PI
+    private static final float FP_TO_DEG = 0.017453292f; // 180/PI
 
     float[] mStack;
     float[] mLocalStack = new float[128];
@@ -106,20 +101,13 @@
         int eval(int sp);
     }
 
-
     /**
-     * Evaluate a float expression
-     * This system works by processing an Array of float (float[])
-     * in reverse polish notation (rpn)
-     * Within that array some floats are commands
-     * they are encoded within an NaN.
-     * After processing the array the last item on the array is returned.
-     * The system supports variables allowing expressions like.
-     * sin(sqrt(x*x+y*y))/sqrt(x*x+y*y)
-     * Where x & y are passe as parameters
-     * Examples:
-     * (1+2)  (1, 2, ADD) adds two numbers returns 3
-     * eval(new float[]{ Var1, Var * }
+     * Evaluate a float expression This system works by processing an Array of float (float[]) in
+     * reverse polish notation (rpn) Within that array some floats are commands they are encoded
+     * within an NaN. After processing the array the last item on the array is returned. The system
+     * supports variables allowing expressions like. sin(sqrt(x*x+y*y))/sqrt(x*x+y*y) Where x & y
+     * are passe as parameters Examples: (1+2) (1, 2, ADD) adds two numbers returns 3 eval(new
+     * float[]{ Var1, Var * }
      *
      * @param exp
      * @param var
@@ -140,7 +128,6 @@
         return mStack[sp];
     }
 
-
     /**
      * Evaluate a float expression
      *
@@ -149,8 +136,9 @@
      * @param var
      * @return
      */
-    public float eval(CollectionsAccess ca, float[] exp, float... var) {
-        mStack = exp;
+    public float eval(CollectionsAccess ca, float[] exp, int len, float... var) {
+        System.arraycopy(exp, 0, mLocalStack, 0, len);
+        mStack = mLocalStack;
         mVar = var;
         mCollectionsAccess = ca;
         int sp = -1;
@@ -159,7 +147,7 @@
             float v = mStack[i];
             if (Float.isNaN(v)) {
                 int id = fromNaN(v);
-                if ((id & ID_REGION_MASK) != ID_REGION_ARRAY) {
+                if ((id & NanMap.ID_REGION_MASK) != NanMap.ID_REGION_ARRAY) {
                     sp = mOps[id - OFFSET].eval(sp);
                 } else {
                     mStack[++sp] = v;
@@ -171,6 +159,34 @@
         return mStack[sp];
     }
 
+    /**
+     * Evaluate a float expression
+     *
+     * @param ca
+     * @param exp
+     * @return
+     */
+    public float eval(CollectionsAccess ca, float[] exp, int len) {
+        System.arraycopy(exp, 0, mLocalStack, 0, len);
+        mStack = mLocalStack;
+        mCollectionsAccess = ca;
+        int sp = -1;
+
+        for (int i = 0; i < len; i++) {
+            float v = mStack[i];
+            if (Float.isNaN(v)) {
+                int id = fromNaN(v);
+                if ((id & NanMap.ID_REGION_MASK) != NanMap.ID_REGION_ARRAY) {
+                    sp = mOps[id - OFFSET].eval(sp);
+                } else {
+                    mStack[++sp] = v;
+                }
+            } else {
+                mStack[++sp] = v;
+            }
+        }
+        return mStack[sp];
+    }
 
     private int dereference(CollectionsAccess ca, int id, int sp) {
         mStack[sp] = ca.getFloatValue(id, (int) (mStack[sp]));
@@ -238,11 +254,11 @@
                 mStack[sp - 1] = mStack[sp - 1] * mStack[sp];
                 return sp - 1;
             },
-            (sp) -> {  // DIV
+            (sp) -> { // DIV
                 mStack[sp - 1] = mStack[sp - 1] / mStack[sp];
                 return sp - 1;
             },
-            (sp) -> {  // MOD
+            (sp) -> { // MOD
                 mStack[sp - 1] = mStack[sp - 1] % mStack[sp];
                 return sp - 1;
             },
@@ -327,13 +343,11 @@
                 return sp - 2;
             },
             (sp) -> { // Ternary conditional
-                mStack[sp - 2] = (mStack[sp] > 0)
-                        ? mStack[sp - 1] : mStack[sp - 2];
+                mStack[sp - 2] = (mStack[sp] > 0) ? mStack[sp - 1] : mStack[sp - 2];
                 return sp - 2;
             },
             (sp) -> { // CLAMP(min,max, val)
-                mStack[sp - 2] = Math.min(Math.max(mStack[sp - 2], mStack[sp]),
-                        mStack[sp - 1]);
+                mStack[sp - 2] = Math.min(Math.max(mStack[sp - 2], mStack[sp]), mStack[sp - 1]);
                 return sp - 2;
             },
             (sp) -> { // CBRT cuberoot
@@ -354,7 +368,7 @@
             },
             (sp) -> { // A_DEREF
                 int id = fromNaN(mStack[sp]);
-                mStack[sp] = mCollectionsAccess.getFloatValue(id, (int) (mStack[sp - 1]));
+                mStack[sp] = mCollectionsAccess.getFloatValue(id, (int) mStack[sp - 1]);
                 return sp - 1;
             },
             (sp) -> { // A_MAX
@@ -399,11 +413,9 @@
             },
             (sp) -> { // A_LEN
                 int id = fromNaN(mStack[sp]);
-                mStack[sp] = mCollectionsAccess.getFloatsLength(id);
+                mStack[sp] = mCollectionsAccess.getListLength(id);
                 return sp;
             },
-
-
             (sp) -> { // first var =
                 mStack[sp] = mVar[0];
                 return sp;
@@ -492,7 +504,8 @@
                     s.append(toMathName(v));
                 } else {
                     int id = fromNaN(v);
-                    String idString = (id > ID_REGION_ARRAY) ? ("A_" + (id & 0xFFFFF)) : "" + id;
+                    String idString =
+                            (id > NanMap.ID_REGION_ARRAY) ? ("A_" + (id & 0xFFFFF)) : "" + id;
                     s.append("[");
                     s.append(idString);
                     s.append("]");
@@ -513,7 +526,7 @@
     }
 
     static String toString(float[] exp, int sp) {
-        String[] str = new String[exp.length];
+        //        String[] str = new String[exp.length];
         if (Float.isNaN(exp[sp])) {
             int id = fromNaN(exp[sp]) - OFFSET;
             switch (NO_OF_OPS[id]) {
@@ -523,24 +536,38 @@
                     return sNames.get(id) + "(" + toString(exp, sp + 1) + ") ";
                 case 2:
                     if (infix(id)) {
-                        return "(" + toString(exp, sp + 1)
-                                + sNames.get(id) + " "
-                                + toString(exp, sp + 2) + ") ";
+                        return "("
+                                + toString(exp, sp + 1)
+                                + sNames.get(id)
+                                + " "
+                                + toString(exp, sp + 2)
+                                + ") ";
                     } else {
-                        return sNames.get(id) + "("
-                                + toString(exp, sp + 1) + ", "
-                                + toString(exp, sp + 2) + ")";
+                        return sNames.get(id)
+                                + "("
+                                + toString(exp, sp + 1)
+                                + ", "
+                                + toString(exp, sp + 2)
+                                + ")";
                     }
                 case 3:
                     if (infix(id)) {
-                        return "((" + toString(exp, sp + 1) + ") ? "
-                                + toString(exp, sp + 2) + ":"
-                                + toString(exp, sp + 3) + ")";
+                        return "(("
+                                + toString(exp, sp + 1)
+                                + ") ? "
+                                + toString(exp, sp + 2)
+                                + ":"
+                                + toString(exp, sp + 3)
+                                + ")";
                     } else {
                         return sNames.get(id)
-                                + "(" + toString(exp, sp + 1)
-                                + ", " + toString(exp, sp + 2)
-                                + ", " + toString(exp, sp + 3) + ")";
+                                + "("
+                                + toString(exp, sp + 1)
+                                + ", "
+                                + toString(exp, sp + 2)
+                                + ", "
+                                + toString(exp, sp + 3)
+                                + ")";
                     }
             }
         }
@@ -549,12 +576,40 @@
 
     static final int[] NO_OF_OPS = {
             -1, // no op
-            2, 2, 2, 2, 2, // + - * / %
-            2, 2, 2,  // min max, power
-            1, 1, 1, 1, 1, 1, 1, 1,  //sqrt,abs,CopySign,exp,floor,log,ln
-            1, 1, 1, 1, 1, 1, 1, 2,  // round,sin,cos,tan,asin,acos,atan,atan2
-            3, 3, 3, 1, 1, 1, 1,
-            0, 0, 0 // mad, ?:,
+            2,
+            2,
+            2,
+            2,
+            2, // + - * / %
+            2,
+            2,
+            2, // min max, power
+            1,
+            1,
+            1,
+            1,
+            1,
+            1,
+            1,
+            1, // sqrt,abs,CopySign,exp,floor,log,ln
+            1,
+            1,
+            1,
+            1,
+            1,
+            1,
+            1,
+            2, // round,sin,cos,tan,asin,acos,atan,atan2
+            3,
+            3,
+            3,
+            1,
+            1,
+            1,
+            1,
+            0,
+            0,
+            0 // mad, ?:,
             // a[0],a[1],a[2]
     };
 
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/ArrayAccess.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/ArrayAccess.java
index 4c7cc38..eb5e482 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/ArrayAccess.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/ArrayAccess.java
@@ -15,10 +15,21 @@
  */
 package com.android.internal.widget.remotecompose.core.operations.utilities;
 
+/**
+ * Support a standardized interface to commands that contain arrays All commands that implement
+ * array access will be collected in a map in the state TODO refactor to DataAccess,
+ * FloatArrayAccess, ListAccess, MapAccess
+ */
 public interface ArrayAccess {
     float getFloatValue(int index);
+
+    default int getId(int index) {
+        return 0;
+    }
+
     float[] getFloats();
-    int getFloatsLength();
+
+    int getLength();
 
     default int getIntValue(int index) {
         return (int) getFloatValue(index);
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/CollectionsAccess.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/CollectionsAccess.java
index 9e53126..0128253 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/CollectionsAccess.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/CollectionsAccess.java
@@ -15,12 +15,20 @@
  */
 package com.android.internal.widget.remotecompose.core.operations.utilities;
 
+/**
+ * interface to allow expressions to access collections Todo define a convention for when access is
+ * unavailable
+ */
 public interface CollectionsAccess {
     float getFloatValue(int id, int index);
-    float[] getFloats(int id);
-    int getFloatsLength(int id);
 
-    default int getIntValue(int id, int index) {
-        return (int) getFloatValue(id, index);
+    float[] getFloats(int id);
+
+    int getListLength(int id);
+
+    int getId(int listId, int index);
+
+    default int getIntValue(int listId, int index) {
+        return (int) getFloatValue(listId, index);
     }
 }
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/ColorUtils.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/ColorUtils.java
index fb96cef..937a25d2 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/ColorUtils.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/ColorUtils.java
@@ -16,21 +16,14 @@
 package com.android.internal.widget.remotecompose.core.operations.utilities;
 
 /**
- * These are tools to use long Color as variables
- * long colors are stored a 0xXXXXXXXX XXXXXX??
- * in SRGB the colors are stored 0xAARRGGBB,00000000
- * SRGB color sapce is color space 0
- * Our Color will use color float with a
- * Current android supports
- * SRGB, LINEAR_SRGB, EXTENDED_SRGB, LINEAR_EXTENDED_SRGB, BT709, BT2020,
- * DCI_P3, DISPLAY_P3, NTSC_1953, SMPTE_C, ADOBE_RGB, PRO_PHOTO_RGB, ACES,
- * ACESCG, CIE_XYZ, CIE_LAB, BT2020_HLG, BT2020_PQ 0..17 respectively
+ * These are tools to use long Color as variables long colors are stored a 0xXXXXXXXX XXXXXX?? in
+ * SRGB the colors are stored 0xAARRGGBB,00000000 SRGB color sapce is color space 0 Our Color will
+ * use color float with a Current android supports SRGB, LINEAR_SRGB, EXTENDED_SRGB,
+ * LINEAR_EXTENDED_SRGB, BT709, BT2020, DCI_P3, DISPLAY_P3, NTSC_1953, SMPTE_C, ADOBE_RGB,
+ * PRO_PHOTO_RGB, ACES, ACESCG, CIE_XYZ, CIE_LAB, BT2020_HLG, BT2020_PQ 0..17 respectively
  *
- * Our color space will be 62 (MAX_ID-1). (0x3E)
- * Storing the default value in SRGB format and having the
- * id of the color between the ARGB values and the 62 i.e.
- * 0xAARRGGBB 00 00 00 3E
- *
+ * <p>Our color space will be 62 (MAX_ID-1). (0x3E) Storing the default value in SRGB format and
+ * having the id of the color between the ARGB values and the 62 i.e. 0xAARRGGBB 00 00 00 3E
  */
 public class ColorUtils {
     public static int RC_COLOR = 62;
@@ -53,6 +46,7 @@
 
     /**
      * get default color from long color
+     *
      * @param color
      * @return
      */
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/DataMap.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/DataMap.java
new file mode 100644
index 0000000..24f17d7
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/DataMap.java
@@ -0,0 +1,46 @@
+/*
+ * 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.widget.remotecompose.core.operations.utilities;
+
+public class DataMap {
+    public String[] mNames;
+    public int[] mIds;
+    public byte[] mTypes;
+
+    public DataMap(String[] names, byte[] types, int[] ids) {
+        mNames = names;
+        mTypes = types;
+        mIds = ids;
+    }
+
+    public int getPos(String str) {
+        for (int i = 0; i < mNames.length; i++) {
+            String name = mNames[i];
+            if (str.equals(name)) {
+                return i;
+            }
+        }
+        return -1;
+    }
+
+    public byte getType(int pos) {
+        return mTypes[pos];
+    }
+
+    public int getId(int pos) {
+        return mIds[pos];
+    }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/ImageScaling.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/ImageScaling.java
new file mode 100644
index 0000000..00c87c1
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/ImageScaling.java
@@ -0,0 +1,226 @@
+/*
+ * 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.widget.remotecompose.core.operations.utilities;
+
+/** Implement the scaling logic for Compose Image or ImageView */
+public class ImageScaling {
+
+    private static final boolean DEBUG = false;
+
+    public static final int SCALE_NONE = 0;
+    public static final int SCALE_INSIDE = 1;
+    public static final int SCALE_FILL_WIDTH = 2;
+    public static final int SCALE_FILL_HEIGHT = 3;
+    public static final int SCALE_FIT = 4;
+    public static final int SCALE_CROP = 5;
+    public static final int SCALE_FILL_BOUNDS = 6;
+    public static final int SCALE_FIXED_SCALE = 7;
+
+    private float mSrcLeft;
+    private float mSrcTop;
+    private float mSrcRight;
+    private float mSrcBottom;
+    private float mDstLeft;
+    private float mDstTop;
+    private float mDstRight;
+    private float mDstBottom;
+    private float mScaleFactor;
+    private int mScaleType;
+
+    public float mFinalDstLeft;
+    public float mFinalDstTop;
+    public float mFinalDstRight;
+    public float mFinalDstBottom;
+
+    public ImageScaling() {}
+
+    public ImageScaling(
+            float srcLeft,
+            float srcTop,
+            float srcRight,
+            float srcBottom,
+            float dstLeft,
+            float dstTop,
+            float dstRight,
+            float dstBottom,
+            int type,
+            float scale) {
+
+        mSrcLeft = srcLeft;
+        mSrcTop = srcTop;
+        mSrcRight = srcRight;
+        mSrcBottom = srcBottom;
+        mDstLeft = dstLeft;
+        mDstTop = dstTop;
+        mDstRight = dstRight;
+        mDstBottom = dstBottom;
+        mScaleType = type;
+        mScaleFactor = scale;
+        adjustDrawToType();
+    }
+
+    public void setup(
+            float srcLeft,
+            float srcTop,
+            float srcRight,
+            float srcBottom,
+            float dstLeft,
+            float dstTop,
+            float dstRight,
+            float dstBottom,
+            int type,
+            float scale) {
+
+        mSrcLeft = srcLeft;
+        mSrcTop = srcTop;
+        mSrcRight = srcRight;
+        mSrcBottom = srcBottom;
+        mDstLeft = dstLeft;
+        mDstTop = dstTop;
+        mDstRight = dstRight;
+        mDstBottom = dstBottom;
+        mScaleType = type;
+        mScaleFactor = scale;
+        adjustDrawToType();
+    }
+
+    static String str(float v) {
+        String s = "  " + (int) v;
+        return s.substring(s.length() - 3);
+    }
+
+    void print(String str, float left, float top, float right, float bottom) {
+        String s = str;
+        s += str(left) + ", " + str(top) + ", " + str(right) + ", " + str(bottom) + ", ";
+        s += " [" + str(right - left) + " x " + str(bottom - top) + "]";
+        System.out.println(s);
+    }
+
+    /** This adjust destnation on the DrawBitMapInt to support all contentScale types */
+    private void adjustDrawToType() {
+        int sw = (int) (mSrcRight - mSrcLeft);
+        int sh = (int) (mSrcBottom - mSrcTop);
+        float width = mDstRight - mDstLeft;
+        float height = mDstBottom - mDstTop;
+        int dw = (int) width;
+        int dh = (int) height;
+        int dLeft = 0;
+        int dRight = dw;
+        int dTop = 0;
+        int dBottom = dh;
+        if (DEBUG) {
+            print("test rc ", mSrcLeft, mSrcTop, mSrcRight, mSrcBottom);
+            print("test dst ", mDstLeft, mDstTop, mDstRight, mDstBottom);
+        }
+
+        switch (mScaleType) {
+            case SCALE_NONE:
+                dh = sh;
+                dw = sw;
+                dTop = ((int) height - dh) / 2;
+                dBottom = dh + dTop;
+                dLeft = ((int) width - dw) / 2;
+                dRight = dw + dLeft;
+                break;
+            case SCALE_INSIDE:
+                if (dh > sh && dw > sw) {
+                    dh = sh;
+                    dw = sw;
+                } else if (sw * height > width * sh) { // width dominated
+                    dh = (dw * sh) / sw;
+                } else {
+                    dw = (dh * sw) / sh;
+                }
+                dTop = ((int) height - dh) / 2;
+                dBottom = dh + dTop;
+                dLeft = ((int) width - dw) / 2;
+                dRight = dw + dLeft;
+                break;
+            case SCALE_FILL_WIDTH:
+                dh = (dw * sh) / sw;
+
+                dTop = ((int) height - dh) / 2;
+                dBottom = dh + dTop;
+                dLeft = ((int) width - dw) / 2;
+                dRight = dw + dLeft;
+                break;
+            case SCALE_FILL_HEIGHT:
+                dw = (dh * sw) / sh;
+
+                dTop = ((int) height - dh) / 2;
+                dBottom = dh + dTop;
+                dLeft = ((int) width - dw) / 2;
+                dRight = dw + dLeft;
+                break;
+            case SCALE_FIT:
+                if (sw * height > width * sh) { // width dominated
+                    dh = (dw * sh) / sw;
+                    dTop = ((int) height - dh) / 2;
+                    dBottom = dh + dTop;
+                } else {
+                    dw = (dh * sw) / sh;
+                    dLeft = ((int) width - dw) / 2;
+                    dRight = dw + dLeft;
+                }
+                break;
+            case SCALE_CROP:
+                if (sw * height < width * sh) { // width dominated
+                    dh = (dw * sh) / sw;
+                    dTop = ((int) height - dh) / 2;
+                    dBottom = dh + dTop;
+                } else {
+                    dw = (dh * sw) / sh;
+                    dLeft = ((int) width - dw) / 2;
+                    dRight = dw + dLeft;
+                }
+                break;
+            case SCALE_FILL_BOUNDS:
+                // do nothing
+                break;
+            case SCALE_FIXED_SCALE:
+                dh = (int) (sh * mScaleFactor);
+                dw = (int) (sw * mScaleFactor);
+                dTop = ((int) height - dh) / 2;
+                dBottom = dh + dTop;
+                dLeft = ((int) width - dw) / 2;
+                dRight = dw + dLeft;
+                break;
+        }
+
+        mFinalDstRight = dRight + mDstLeft;
+        mFinalDstLeft = dLeft + mDstLeft;
+        mFinalDstBottom = dBottom + mDstTop;
+        mFinalDstTop = dTop + mDstTop;
+
+        if (DEBUG) {
+            print("test  out ", mFinalDstLeft, mFinalDstTop, mFinalDstRight, mFinalDstBottom);
+        }
+    }
+
+    public static String typeToString(int type) {
+        String[] typeString = {
+            "none",
+            "inside",
+            "fill_width",
+            "fill_height",
+            "fit",
+            "crop",
+            "fill_bounds",
+            "fixed_scale"
+        };
+        return typeString[type];
+    }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/IntFloatMap.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/IntFloatMap.java
index b2d714e..3b4ece9 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/IntFloatMap.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/IntFloatMap.java
@@ -32,9 +32,7 @@
         mValues = new float[DEFAULT_CAPACITY];
     }
 
-    /**
-     * clear the map
-     */
+    /** clear the map */
     public void clear() {
         Arrays.fill(mKeys, NOT_PRESENT);
         Arrays.fill(mValues, Float.NaN); // not strictly necessary but defensive
@@ -78,8 +76,7 @@
         int index = findKey(key);
         if (index == -1) {
             return 0;
-        } else
-            return mValues[index];
+        } else return mValues[index];
     }
 
     /**
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/IntIntMap.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/IntIntMap.java
index 606dc78..68cd0e6 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/IntIntMap.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/IntIntMap.java
@@ -31,9 +31,7 @@
         mValues = new int[DEFAULT_CAPACITY];
     }
 
-    /**
-     * clear the map
-     */
+    /** clear the map */
     public void clear() {
         Arrays.fill(mKeys, NOT_PRESENT);
         Arrays.fill(mValues, 0);
@@ -77,8 +75,7 @@
         int index = findKey(key);
         if (index == -1) {
             return 0;
-        } else
-            return mValues[index];
+        } else return mValues[index];
     }
 
     /**
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/IntMap.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/IntMap.java
index 0512fa6..84e7843 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/IntMap.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/IntMap.java
@@ -42,7 +42,7 @@
         mSize = 0;
     }
 
-    public T put(int key,  T value)  {
+    public T put(int key, T value) {
         if (key == NOT_PRESENT) throw new IllegalArgumentException("Key cannot be NOT_PRESENT");
         if (mSize > mKeys.length * LOAD_FACTOR) {
             resize();
@@ -50,24 +50,23 @@
         return insert(key, value);
     }
 
-    public  T get(int key) {
+    public T get(int key) {
         int index = findKey(key);
         if (index == -1) {
-            return  null;
-        } else
-            return mValues.get(index);
+            return null;
+        } else return mValues.get(index);
     }
 
     public int size() {
         return mSize;
     }
 
-    private  T insert(int key, T value) {
+    private T insert(int key, T value) {
         int index = hash(key) % mKeys.length;
         while (mKeys[index] != NOT_PRESENT && mKeys[index] != key) {
             index = (index + 1) % mKeys.length;
         }
-        T oldValue =  null;
+        T oldValue = null;
         if (mKeys[index] == NOT_PRESENT) {
             mSize++;
         } else {
@@ -78,7 +77,7 @@
         return oldValue;
     }
 
-    private  int findKey(int key) {
+    private int findKey(int key) {
         int index = hash(key) % mKeys.length;
         while (mKeys[index] != NOT_PRESENT) {
             if (mKeys[index] == key) {
@@ -89,11 +88,11 @@
         return -1;
     }
 
-    private  int hash(int key) {
+    private int hash(int key) {
         return key;
     }
 
-    private   void resize() {
+    private void resize() {
         int[] oldKeys = mKeys;
         ArrayList<T> oldValues = mValues;
         mKeys = new int[(oldKeys.length * 2)];
@@ -111,4 +110,46 @@
             }
         }
     }
+
+    public T remove(int key) {
+        int index = hash(key) % mKeys.length;
+        int initialIndex = index;
+
+        while (mKeys[index] != NOT_PRESENT) {
+            if (mKeys[index] == key) {
+                T oldValue = mValues.get(index);
+                mKeys[index] = NOT_PRESENT;
+                mValues.set(index, null);
+                mSize--;
+
+                // Rehash the cluster of keys following the removed key
+                rehashFrom((index + 1) % mKeys.length);
+                return oldValue;
+            }
+            index = (index + 1) % mKeys.length;
+            if (index == initialIndex) {
+                break; // Avoid infinite loop
+            }
+        }
+        return null; // Key not found
+    }
+
+    private void rehashFrom(int startIndex) {
+        int index = startIndex;
+
+        while (mKeys[index] != NOT_PRESENT) {
+            int keyToRehash = mKeys[index];
+            T valueToRehash = mValues.get(index);
+
+            // Remove the key-value pair from the current position
+            mKeys[index] = NOT_PRESENT;
+            mValues.set(index, null);
+            mSize--;
+
+            // Re-insert the key-value pair
+            insert(keyToRehash, valueToRehash);
+
+            index = (index + 1) % mKeys.length;
+        }
+    }
 }
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/IntegerExpressionEvaluator.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/IntegerExpressionEvaluator.java
index ae61ec1..baa144d 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/IntegerExpressionEvaluator.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/IntegerExpressionEvaluator.java
@@ -18,8 +18,8 @@
 /**
  * High performance Integer expression evaluator
  *
- * The evaluation is based on int opMask, int[]exp
- * exp[i] is an operator if (opMask*(1 << i) != 0)
+ * <p>The evaluation is based on int opMask, int[]exp exp[i] is an operator if (opMask*(1 << i) !=
+ * 0)
  */
 public class IntegerExpressionEvaluator {
     static IntMap<String> sNames = new IntMap<>();
@@ -51,26 +51,25 @@
     public static final int I_IFELSE = OFFSET + 22;
     public static final int I_MAD = OFFSET + 23;
 
-    public static final float LAST_OP = 24;
+    public static final float LAST_OP = 25;
 
     public static final int I_VAR1 = OFFSET + 24;
-    public static final int I_VAR2 = OFFSET + 24;
-
+    public static final int I_VAR2 = OFFSET + 25;
 
     int[] mStack;
     int[] mLocalStack = new int[128];
     int[] mVar;
 
-
     interface Op {
         int eval(int sp);
     }
 
     /**
      * Evaluate an integer expression
+     *
      * @param mask bits that are operators
-     * @param exp rpn sequence of values and operators
-     * @param var variables if the expression is a function
+     * @param exp  rpn sequence of values and operators
+     * @param var  variables if the expression is a function
      * @return return the results of evaluating the expression
      */
     public int eval(int mask, int[] exp, int... var) {
@@ -90,10 +89,11 @@
 
     /**
      * Evaluate a integer expression
+     *
      * @param mask bits that are operators
-     * @param exp rpn sequence of values and operators
-     * @param len the number of values in the expression
-     * @param var variables if the expression is a function
+     * @param exp  rpn sequence of values and operators
+     * @param len  the number of values in the expression
+     * @param var  variables if the expression is a function
      * @return return the results of evaluating the expression
      */
     public int eval(int mask, int[] exp, int len, int... var) {
@@ -114,9 +114,10 @@
 
     /**
      * Evaluate a int expression
+     *
      * @param opMask bits that are operators
-     * @param exp rpn sequence of values and operators
-     * @param var variables if the expression is a function
+     * @param exp    rpn sequence of values and operators
+     * @param var    variables if the expression is a function
      * @return return the results of evaluating the expression
      */
     public int evalDB(int opMask, int[] exp, int... var) {
@@ -150,11 +151,11 @@
                 mStack[sp - 1] = mStack[sp - 1] * mStack[sp];
                 return sp - 1;
             },
-            (sp) -> {  // DIV
+            (sp) -> { // DIV
                 mStack[sp - 1] = mStack[sp - 1] / mStack[sp];
                 return sp - 1;
             },
-            (sp) -> {  // MOD
+            (sp) -> { // MOD
                 mStack[sp - 1] = mStack[sp - 1] % mStack[sp];
                 return sp - 1;
             },
@@ -183,8 +184,7 @@
                 return sp - 1;
             },
             (sp) -> { // COPY_SIGN copy the sing of (using bit magic)
-                mStack[sp - 1] = (mStack[sp - 1] ^ (mStack[sp] >> 31))
-                        - (mStack[sp] >> 31);
+                mStack[sp - 1] = (mStack[sp - 1] ^ (mStack[sp] >> 31)) - (mStack[sp] >> 31);
                 return sp - 1;
             },
             (sp) -> { // MIN
@@ -219,22 +219,18 @@
                 mStack[sp] = (mStack[sp] >> 31) | (-mStack[sp] >>> 31);
                 return sp;
             },
-
             (sp) -> { // CLAMP(min,max, val)
-                mStack[sp - 2] = Math.min(Math.max(mStack[sp - 2], mStack[sp]),
-                        mStack[sp - 1]);
+                mStack[sp - 2] = Math.min(Math.max(mStack[sp - 2], mStack[sp]), mStack[sp - 1]);
                 return sp - 2;
             },
             (sp) -> { // Ternary conditional
-                mStack[sp - 2] = (mStack[sp] > 0)
-                        ? mStack[sp - 1] : mStack[sp - 2];
+                mStack[sp - 2] = (mStack[sp] > 0) ? mStack[sp - 1] : mStack[sp - 2];
                 return sp - 2;
             },
             (sp) -> { // MAD
                 mStack[sp - 2] = mStack[sp] + mStack[sp - 1] * mStack[sp - 2];
                 return sp - 2;
             },
-
             (sp) -> { // first var =
                 mStack[sp] = mVar[0];
                 return sp;
@@ -296,7 +292,7 @@
      * Convert an expression encoded as an array of ints int to a string
      *
      * @param opMask bits that are operators
-     * @param exp rpn sequence of values and operators
+     * @param exp    rpn sequence of values and operators
      * @param labels String that represent the variable names
      * @return
      */
@@ -328,7 +324,7 @@
      * Convert an expression encoded as an array of ints int ot a string
      *
      * @param opMask bit mask of operators vs commands
-     * @param exp rpn sequence of values and operators
+     * @param exp    rpn sequence of values and operators
      * @return string representation of the expression
      */
     public static String toString(int opMask, int[] exp) {
@@ -357,8 +353,9 @@
 
     /**
      * This creates an infix string expression
+     *
      * @param opMask The bits that are operators
-     * @param exp the array of expressions
+     * @param exp    the array of expressions
      * @return infix string
      */
     public static String toStringInfix(int opMask, int[] exp) {
@@ -375,24 +372,39 @@
                     return sNames.get(id) + "(" + toString(mask, exp, sp - 1) + ") ";
                 case 2:
                     if (infix(id)) {
-                        return "(" + toString(mask, exp, sp - 2)
-                                + " " + sNames.get(id) + " "
-                                + toString(mask, exp, sp - 1) + ") ";
+                        return "("
+                                + toString(mask, exp, sp - 2)
+                                + " "
+                                + sNames.get(id)
+                                + " "
+                                + toString(mask, exp, sp - 1)
+                                + ") ";
                     } else {
-                        return sNames.get(id) + "("
-                                + toString(mask, exp, sp - 2) + ", "
-                                + toString(mask, exp, sp - 1) + ")";
+                        return sNames.get(id)
+                                + "("
+                                + toString(mask, exp, sp - 2)
+                                + ", "
+                                + toString(mask, exp, sp - 1)
+                                + ")";
                     }
                 case 3:
                     if (infix(id)) {
-                        return "((" + toString(mask, exp, sp + 3) + ") ? "
-                                + toString(mask, exp, sp - 2) + ":"
-                                + toString(mask, exp, sp - 1) + ")";
+                        return "(("
+                                + toString(mask, exp, sp + 3)
+                                + ") ? "
+                                + toString(mask, exp, sp - 2)
+                                + ":"
+                                + toString(mask, exp, sp - 1)
+                                + ")";
                     } else {
                         return sNames.get(id)
-                                + "(" + toString(mask, exp, sp - 3)
-                                + ", " + toString(mask, exp, sp - 2)
-                                + ", " + toString(mask, exp, sp - 1) + ")";
+                                + "("
+                                + toString(mask, exp, sp - 3)
+                                + ", "
+                                + toString(mask, exp, sp - 2)
+                                + ", "
+                                + toString(mask, exp, sp - 1)
+                                + ")";
                     }
             }
         }
@@ -401,11 +413,32 @@
 
     static final int[] NO_OF_OPS = {
             -1, // no op
-            2, 2, 2, 2, 2, // + - * / %
-            2, 2, 2, 2, 2, 2, 2, 2, 2, //<<, >> , >>> , | , &, ^, min max
-            1, 1, 1, 1, 1, 1,  // neg, abs, ++, -- , not , sign
-            3, 3, 3, // clamp, ifElse, mad,
-            0, 0, 0 // mad, ?:,
+            2,
+            2,
+            2,
+            2,
+            2, // + - * / %
+            2,
+            2,
+            2,
+            2,
+            2,
+            2,
+            2,
+            2,
+            2, // <<, >> , >>> , | , &, ^, min max
+            1,
+            1,
+            1,
+            1,
+            1,
+            1, // neg, abs, ++, -- , not , sign
+            3,
+            3,
+            3, // clamp, ifElse, mad,
+            0,
+            0,
+            0 // mad, ?:,
             // a[0],a[1],a[2]
     };
 
@@ -416,13 +449,14 @@
      * @return true if the operator is infix
      */
     static boolean infix(int n) {
-        return ((n < 12));
+        return n < 12;
     }
 
     /**
      * is it an id or operation
+     *
      * @param opMask the bits that mark elements as an operation
-     * @param i the bit to check
+     * @param i      the bit to check
      * @return true if the bit is 1
      */
     public static boolean isOperation(int opMask, int i) {
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/NanMap.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/NanMap.java
index 1e669c6..0616cc7 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/NanMap.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/NanMap.java
@@ -18,17 +18,12 @@
 import com.android.internal.widget.remotecompose.core.operations.Utils;
 
 /**
- * This defines the major id maps and ranges used by remote compose
- * Generally ids ranging from 1 ... 7FFFFF (4095) are for ids
- * The data range is divided int to bits
- * 0xxxxx are allocated for Predefined Global System Variables
- * 1xxxxx are allocated to normal variables
- * 2xxxxx are allocated to List&MAPS (Arrays of stuff)
- * 3xxxxx are allocated to path & float operations
- * 4xxxxx,5xxxxx,7xxxxx are reserved for future use
- * 0x1000-0x1100 are used for path operations in PathData
- * 0x1100-0x1200 are used for math operations in Animated float
- * 0x
+ * This defines the major id maps and ranges used by remote compose Generally ids ranging from 1 ...
+ * 7FFFFF (4095) are for ids The data range is divided int to bits 0xxxxx are allocated for
+ * Predefined Global System Variables 1xxxxx are allocated to normal variables 2xxxxx are allocated
+ * to List&MAPS (Arrays of stuff) 3xxxxx are allocated to path & float operations
+ * 4xxxxx,5xxxxx,7xxxxx are reserved for future use 0x1000-0x1100 are used for path operations in
+ * PathData 0x1100-0x1200 are used for math operations in Animated float 0x
  */
 public class NanMap {
     public static final int MOVE = 0x300_000;
@@ -71,7 +66,6 @@
     public static final int ID_REGION_MASK = 0x700000;
     public static final int ID_REGION_ARRAY = 0x200000;
 
-
     /**
      * Get ID from Nan float
      *
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/StringSerializer.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/StringSerializer.java
index fb90781..ab7576e 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/StringSerializer.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/StringSerializer.java
@@ -15,15 +15,14 @@
  */
 package com.android.internal.widget.remotecompose.core.operations.utilities;
 
-/**
- * Utility serializer maintaining an indent buffer
- */
+/** Utility serializer maintaining an indent buffer */
 public class StringSerializer {
     StringBuffer mBuffer = new StringBuffer();
     String mIndentBuffer = "                                                                      ";
 
     /**
      * Append some content to the current buffer
+     *
      * @param indent the indentation level to use
      * @param content content to append
      */
@@ -35,17 +34,17 @@
         mBuffer.append("\n");
     }
 
-    /**
-     * Reset the buffer
-     */
+    /** Reset the buffer */
     public void reset() {
         mBuffer = new StringBuffer();
     }
 
     /**
      * Return a string representation of the buffer
+     *
      * @return string representation
      */
+    @Override
     public String toString() {
         return mBuffer.toString();
     }
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/StringUtils.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/StringUtils.java
index 8dd5405..f2ccb40 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/StringUtils.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/StringUtils.java
@@ -17,26 +17,21 @@
 
 import java.util.Arrays;
 
-/**
- * Utilities for string manipulation
- */
+/** Utilities for string manipulation */
 public class StringUtils {
     /**
-     * Converts a float into a string.
-     * Providing a defined number of characters before and after the
+     * Converts a float into a string. Providing a defined number of characters before and after the
      * decimal point.
      *
-     * @param value              The value to convert to string
+     * @param value The value to convert to string
      * @param beforeDecimalPoint digits before the decimal point
-     * @param afterDecimalPoint  digits after the decimal point
-     * @param pre                character to pad width 0 = no pad typically ' ' or '0'
-     * @param post               character to pad width 0 = no pad typically ' ' or '0'
+     * @param afterDecimalPoint digits after the decimal point
+     * @param pre character to pad width 0 = no pad typically ' ' or '0'
+     * @param post character to pad width 0 = no pad typically ' ' or '0'
      * @return
      */
-    public static String floatToString(float value,
-                                       int beforeDecimalPoint,
-                                       int afterDecimalPoint,
-                                       char pre, char post) {
+    public static String floatToString(
+            float value, int beforeDecimalPoint, int afterDecimalPoint, char pre, char post) {
 
         int integerPart = (int) value;
         float fractionalPart = value % 1;
@@ -52,7 +47,6 @@
                 integerPartString = new String(pad) + integerPartString;
             }
 
-
         } else if (iLen > beforeDecimalPoint) {
             integerPartString = integerPartString.substring(iLen - beforeDecimalPoint);
         }
@@ -68,7 +62,7 @@
         fractionalPart = Math.round(fractionalPart);
 
         for (int i = 0; i < afterDecimalPoint; i++) {
-            fractionalPart *= .1;
+            fractionalPart *= .1F;
         }
 
         String fact = Float.toString(fractionalPart);
@@ -92,5 +86,4 @@
 
         return integerPartString + "." + fact;
     }
-
 }
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/BounceCurve.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/BounceCurve.java
index c3cd5ae9..3161aa1 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/BounceCurve.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/BounceCurve.java
@@ -15,9 +15,7 @@
  */
 package com.android.internal.widget.remotecompose.core.operations.utilities.easing;
 
-/**
- * Provide a specific bouncing easing function
- */
+/** Provide a specific bouncing easing function */
 public class BounceCurve extends Easing {
     private static final float N1 = 7.5625f;
     private static final float D1 = 2.75f;
@@ -63,5 +61,4 @@
         }
         return 0f;
     }
-
 }
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/CubicEasing.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/CubicEasing.java
index fd1ee03..60a59cf 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/CubicEasing.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/CubicEasing.java
@@ -16,7 +16,6 @@
 package com.android.internal.widget.remotecompose.core.operations.utilities.easing;
 
 class CubicEasing extends Easing {
-    float mType = 0;
     float mX1 = 0f;
     float mY1 = 0f;
     float mX2 = 0f;
@@ -92,25 +91,24 @@
         return mY1 * f1 + mY2 * f2 + f3;
     }
 
-    private float getDiffX(float t) {
-        float t1 = 1 - t;
-        return 3 * t1 * t1 * mX1 + 6 * t1 * t * (mX2 - mX1) + 3 * t * t * (1 - mX2);
-    }
+    //    private float getDiffX(float t) {
+    //        float t1 = 1 - t;
+    //        return 3 * t1 * t1 * mX1 + 6 * t1 * t * (mX2 - mX1) + 3 * t * t * (1 - mX2);
+    //    }
 
-    private float getDiffY(float t) {
-        float t1 = 1 - t;
-        return 3 * t1 * t1 * mY1 + 6 * t1 * t * (mY2 - mY1) + 3 * t * t * (1 - mY2);
-    }
+    //    private float getDiffY(float t) {
+    //        float t1 = 1 - t;
+    //        return 3 * t1 * t1 * mY1 + 6 * t1 * t * (mY2 - mY1) + 3 * t * t * (1 - mY2);
+    //    }
 
-    /**
-     * binary search for the region and linear interpolate the answer
-     */
+    /** binary search for the region and linear interpolate the answer */
+    @Override
     public float getDiff(float x) {
         float t = 0.5f;
         float range = 0.5f;
         while (range > D_ERROR) {
             float tx = getX(t);
-            range *= 0.5;
+            range *= 0.5f;
             if (tx < x) {
                 t += range;
             } else {
@@ -124,9 +122,8 @@
         return (y2 - y1) / (x2 - x1);
     }
 
-    /**
-     * binary search for the region and linear interpolate the answer
-     */
+    /** binary search for the region and linear interpolate the answer */
+    @Override
     public float get(float x) {
         if (x <= 0.0f) {
             return 0f;
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/Easing.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/Easing.java
index 4ed9550..d8bc83e 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/Easing.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/Easing.java
@@ -15,19 +15,14 @@
  */
 package com.android.internal.widget.remotecompose.core.operations.utilities.easing;
 
-/**
- * The standard interface to Easing functions
- */
+/** The standard interface to Easing functions */
 public abstract class Easing {
     int mType;
-    /**
-     * get the value at point x
-     */
+
+    /** get the value at point x */
     public abstract float get(float x);
 
-    /**
-     * get the slope of the easing function at at x
-     */
+    /** get the slope of the easing function at at x */
     public abstract float getDiff(float x);
 
     public int getType() {
@@ -44,5 +39,4 @@
     public static final int SPLINE_CUSTOM = 12;
     public static final int EASE_OUT_BOUNCE = 13;
     public static final int EASE_OUT_ELASTIC = 14;
-
 }
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/ElasticOutCurve.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/ElasticOutCurve.java
index e269583..d53cff5 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/ElasticOutCurve.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/ElasticOutCurve.java
@@ -15,9 +15,7 @@
  */
 package com.android.internal.widget.remotecompose.core.operations.utilities.easing;
 
-/**
- * Provide a bouncing Easing function
- */
+/** Provide a bouncing Easing function */
 public class ElasticOutCurve extends Easing {
     private static final float F_PI = (float) Math.PI;
     private static final float C4 = 2 * F_PI / 3;
@@ -31,9 +29,7 @@
         }
         if (x >= 1) {
             return 1.0f;
-        } else
-            return (float) (Math.pow(2.0f, -10 * x)
-                    * Math.sin((x * 10 - 0.75f) * C4) + 1);
+        } else return (float) (Math.pow(2.0f, -10 * x) * Math.sin((x * 10 - 0.75f) * C4) + 1);
     }
 
     @Override
@@ -41,9 +37,11 @@
         if (x < 0 || x > 1) {
             return 0.0f;
         } else
-            return (float) ((5 * Math.pow(2.0f, (1 - 10 * x))
-                    * (LOG_8 * Math.cos(TWENTY_PI * x / 3) + 2
-                    * F_PI * Math.sin(TWENTY_PI * x / 3))
-                    / 3));
+            return (float)
+                    (5
+                            * Math.pow(2.0f, 1 - 10 * x)
+                            * (LOG_8 * Math.cos(TWENTY_PI * x / 3)
+                                    + 2 * F_PI * Math.sin(TWENTY_PI * x / 3))
+                            / 3);
     }
 }
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/FloatAnimation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/FloatAnimation.java
index 98dbf00..a29b8af 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/FloatAnimation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/FloatAnimation.java
@@ -15,9 +15,7 @@
  */
 package com.android.internal.widget.remotecompose.core.operations.utilities.easing;
 
-/**
- * Support Animation of the FloatExpression
- */
+/** Support Animation of the FloatExpression */
 public class FloatAnimation extends Easing {
     float[] mSpec;
     // mSpec[0] = duration
@@ -25,12 +23,12 @@
     // mSpec[2..1+num_of_param] params
     // mSpec[2+num_of_param] starting Value
     Easing mEasingCurve;
-    private int mType = CUBIC_STANDARD;
+
     private float mDuration = 1;
     private float mWrap = Float.NaN;
     private float mInitialValue = Float.NaN;
     private float mTargetValue = Float.NaN;
-    private float mScale = 1;
+    //    private float mScale = 1;
     float mOffset = 0;
 
     @Override
@@ -51,20 +49,19 @@
     }
 
     public FloatAnimation() {
+        mType = CUBIC_STANDARD;
         mEasingCurve = new CubicEasing(mType);
     }
 
     public FloatAnimation(float... description) {
+        mType = CUBIC_STANDARD;
         setAnimationDescription(description);
     }
 
-    public FloatAnimation(int type,
-                          float duration,
-                          float[] description,
-                          float initialValue,
-                          float wrap) {
-        setAnimationDescription(packToFloatArray(duration,
-                type, description, initialValue, wrap));
+    public FloatAnimation(
+            int type, float duration, float[] description, float initialValue, float wrap) {
+        mType = CUBIC_STANDARD;
+        setAnimationDescription(packToFloatArray(duration, type, description, initialValue, wrap));
     }
 
     /**
@@ -76,11 +73,8 @@
      * @param initialValue
      * @return
      */
-    public static float[] packToFloatArray(float duration,
-                                           int type,
-                                           float[] spec,
-                                           float initialValue,
-                                           float wrap) {
+    public static float[] packToFloatArray(
+            float duration, int type, float[] spec, float initialValue, float wrap) {
         int count = 0;
 
         if (!Float.isNaN(initialValue)) {
@@ -104,7 +98,7 @@
         if (duration != 1 || count > 0) {
             count++;
         }
-        if  (!Float.isNaN(wrap) || !Float.isNaN(initialValue)) {
+        if (!Float.isNaN(wrap) || !Float.isNaN(initialValue)) {
             count++;
         }
         float[] ret = new float[count];
@@ -113,11 +107,10 @@
 
         if (ret.length > 0) {
             ret[pos++] = duration;
-
         }
         if (ret.length > 1) {
-            int wrapBit = (Float.isNaN(wrap)) ? 0 : 1;
-            int initBit = (Float.isNaN(initialValue)) ? 0 : 2;
+            int wrapBit = Float.isNaN(wrap) ? 0 : 1;
+            int initBit = Float.isNaN(initialValue) ? 0 : 2;
             int bits = type | ((wrapBit | initBit) << 8);
             ret[pos++] = Float.intBitsToFloat(specLen << 16 | bits);
         }
@@ -137,6 +130,7 @@
 
     /**
      * Create an animation based on a float encoding of the animation
+     *
      * @param description
      */
     public void setAnimationDescription(float[] description) {
@@ -171,11 +165,12 @@
                 mEasingCurve = new CubicEasing(type);
                 break;
             case CUBIC_CUSTOM:
-                mEasingCurve = new CubicEasing(params[offset + 0],
-                        params[offset + 1],
-                        params[offset + 2],
-                        params[offset + 3]
-                );
+                mEasingCurve =
+                        new CubicEasing(
+                                params[offset + 0],
+                                params[offset + 1],
+                                params[offset + 2],
+                                params[offset + 3]);
                 break;
             case EASE_OUT_BOUNCE:
                 mEasingCurve = new BounceCurve(type);
@@ -191,6 +186,7 @@
 
     /**
      * Get the duration the interpolate is to take
+     *
      * @return duration in seconds
      */
     public float getDuration() {
@@ -199,6 +195,7 @@
 
     /**
      * Set the initial Value
+     *
      * @param value
      */
     public void setInitialValue(float value) {
@@ -213,6 +210,7 @@
 
     /**
      * Set the target value to interpolate to
+     *
      * @param value
      */
     public void setTargetValue(float value) {
@@ -236,25 +234,23 @@
 
     private void setScaleOffset() {
         if (!Float.isNaN(mInitialValue) && !Float.isNaN(mTargetValue)) {
-            mScale = (mTargetValue - mInitialValue);
+            //            mScale = (mTargetValue - mInitialValue); // TODO: commented out because
+            // unused.
             mOffset = mInitialValue;
         } else {
-            mScale = 1;
+            //            mScale = 1; // TODO: commented out because its unused
             mOffset = 0;
         }
     }
 
-    /**
-     * get the value at time t in seconds since start
-     */
+    /** get the value at time t in seconds since start */
+    @Override
     public float get(float t) {
-        return mEasingCurve.get(t / mDuration)
-                * (mTargetValue - mInitialValue) + mInitialValue;
+        return mEasingCurve.get(t / mDuration) * (mTargetValue - mInitialValue) + mInitialValue;
     }
 
-    /**
-     * get the slope of the easing function at at x
-     */
+    /** get the slope of the easing function at at x */
+    @Override
     public float getDiff(float t) {
         return mEasingCurve.getDiff(t / mDuration) * (mTargetValue - mInitialValue);
     }
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/GeneralEasing.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/GeneralEasing.java
index 50a7d59..75a6032 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/GeneralEasing.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/GeneralEasing.java
@@ -15,15 +15,14 @@
  */
 package com.android.internal.widget.remotecompose.core.operations.utilities.easing;
 
-/**
- * Provides and interface to create easing functions
- */
-public class GeneralEasing extends Easing{
+/** Provides and interface to create easing functions */
+public class GeneralEasing extends Easing {
     float[] mEasingData = new float[0];
     Easing mEasingCurve = new CubicEasing(CUBIC_STANDARD);
 
     /**
      * Set the curve based on the float encoding of it
+     *
      * @param data
      */
     public void setCurveSpecification(float[] data) {
@@ -47,11 +46,9 @@
                 mEasingCurve = new CubicEasing(type);
                 break;
             case CUBIC_CUSTOM:
-                mEasingCurve = new CubicEasing(mEasingData[1],
-                        mEasingData[2],
-                        mEasingData[3],
-                        mEasingData[5]
-                );
+                mEasingCurve =
+                        new CubicEasing(
+                                mEasingData[1], mEasingData[2], mEasingData[3], mEasingData[5]);
                 break;
             case EASE_OUT_BOUNCE:
                 mEasingCurve = new BounceCurve(type);
@@ -59,23 +56,20 @@
         }
     }
 
-    /**
-     * get the value at point x
-     */
+    /** get the value at point x */
+    @Override
     public float get(float x) {
         return mEasingCurve.get(x);
     }
 
-    /**
-     * get the slope of the easing function at at x
-     */
+    /** get the slope of the easing function at at x */
+    @Override
     public float getDiff(float x) {
         return mEasingCurve.getDiff(x);
     }
 
+    @Override
     public int getType() {
         return mEasingCurve.getType();
     }
-
-
 }
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/MonotonicCurveFit.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/MonotonicCurveFit.java
index 23930b9..9355cac 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/MonotonicCurveFit.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/MonotonicCurveFit.java
@@ -17,12 +17,8 @@
 
 import java.util.Arrays;
 
-/**
- * This performs a spline interpolation in multiple dimensions
- *
- *
- */
-public class MonotonicCurveFit  {
+/** This performs a spline interpolation in multiple dimensions */
+public class MonotonicCurveFit {
     private static final String TAG = "MonotonicCurveFit";
     private double[] mT;
     private double[][] mY;
@@ -32,6 +28,7 @@
 
     /**
      * create a collection of curves
+     *
      * @param time the point along the curve
      * @param y the parameter at those points
      */
@@ -78,6 +75,7 @@
 
     /**
      * Get the position of all curves at time t
+     *
      * @param t
      * @param v
      */
@@ -137,6 +135,7 @@
 
     /**
      * Get the position of all curves at time t
+     *
      * @param t
      * @param v
      */
@@ -196,6 +195,7 @@
 
     /**
      * Get the position of the jth curve at time t
+     *
      * @param t
      * @param j
      * @return
@@ -230,7 +230,6 @@
                 double t1 = mTangent[i][j];
                 double t2 = mTangent[i + 1][j];
                 return interpolate(h, x, y1, y2, t1, t2);
-
             }
         }
         return 0; // should never reach here
@@ -238,6 +237,7 @@
 
     /**
      * Get the slope of all the curves at position t
+     *
      * @param t
      * @param v
      */
@@ -264,11 +264,11 @@
                 break;
             }
         }
-        return;
     }
 
     /**
      * Get the slope of the j curve at position t
+     *
      * @param t
      * @param j
      * @return
@@ -299,34 +299,38 @@
         return mT;
     }
 
-    /**
-     * Cubic Hermite spline
-     */
-    private static double interpolate(double h,
-                                      double x,
-                                      double y1,
-                                      double y2,
-                                      double t1,
-                                      double t2) {
+    /** Cubic Hermite spline */
+    private static double interpolate(
+            double h, double x, double y1, double y2, double t1, double t2) {
         double x2 = x * x;
         double x3 = x2 * x;
-        return -2 * x3 * y2 + 3 * x2 * y2 + 2 * x3 * y1 - 3 * x2 * y1 + y1
-                + h * t2 * x3 + h * t1 * x3 - h * t2 * x2 - 2 * h * t1 * x2
+        return -2 * x3 * y2
+                + 3 * x2 * y2
+                + 2 * x3 * y1
+                - 3 * x2 * y1
+                + y1
+                + h * t2 * x3
+                + h * t1 * x3
+                - h * t2 * x2
+                - 2 * h * t1 * x2
                 + h * t1 * x;
     }
 
-    /**
-     * Cubic Hermite spline slope differentiated
-     */
+    /** Cubic Hermite spline slope differentiated */
     private static double diff(double h, double x, double y1, double y2, double t1, double t2) {
         double x2 = x * x;
-        return -6 * x2 * y2 + 6 * x * y2 + 6 * x2 * y1 - 6 * x * y1 + 3 * h * t2 * x2
-                + 3 * h * t1 * x2 - 2 * h * t2 * x - 4 * h * t1 * x + h * t1;
+        return -6 * x2 * y2
+                + 6 * x * y2
+                + 6 * x2 * y1
+                - 6 * x * y1
+                + 3 * h * t2 * x2
+                + 3 * h * t1 * x2
+                - 2 * h * t2 * x
+                - 4 * h * t1 * x
+                + h * t1;
     }
 
-    /**
-     * This builds a monotonic spline to be used as a wave function
-     */
+    /** This builds a monotonic spline to be used as a wave function */
     public static MonotonicCurveFit buildWave(String configString) {
         // done this way for efficiency
         String str = configString;
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/StepCurve.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/StepCurve.java
index 6ed6548..b459689 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/StepCurve.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/StepCurve.java
@@ -15,14 +15,13 @@
  */
 package com.android.internal.widget.remotecompose.core.operations.utilities.easing;
 
-
 /**
- * This class translates a series of floating point values into a continuous
- * curve for use in an easing function including quantize functions
- * it is used with the "spline(0,0.3,0.3,0.5,...0.9,1)" it should start at 0 and end at one 1
+ * This class translates a series of floating point values into a continuous curve for use in an
+ * easing function including quantize functions it is used with the "spline(0,0.3,0.3,0.5,...0.9,1)"
+ * it should start at 0 and end at one 1
  */
 public class StepCurve extends Easing {
-    private static final boolean DEBUG = false;
+    //    private static final boolean DEBUG = false;
     MonotonicCurveFit mCurveFit;
 
     public StepCurve(float[] params, int offset, int len) {
@@ -55,12 +54,16 @@
 
     @Override
     public float getDiff(float x) {
+        if (x < 0f) return 0;
+        if (x > 1f) return 0;
         return (float) mCurveFit.getSlope(x, 0);
     }
 
-
     @Override
     public float get(float x) {
+        if (x < 0f) return 0;
+        if (x > 1f) return 1;
+
         return (float) mCurveFit.getPos(x, 0);
     }
 }
diff --git a/core/java/com/android/internal/widget/remotecompose/core/types/BooleanConstant.java b/core/java/com/android/internal/widget/remotecompose/core/types/BooleanConstant.java
index 9045bcbd..57a8042 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/types/BooleanConstant.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/types/BooleanConstant.java
@@ -15,23 +15,21 @@
  */
 package com.android.internal.widget.remotecompose.core.types;
 
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.BYTE;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.BYTE;
 
 import com.android.internal.widget.remotecompose.core.Operation;
 import com.android.internal.widget.remotecompose.core.Operations;
 import com.android.internal.widget.remotecompose.core.RemoteContext;
 import com.android.internal.widget.remotecompose.core.WireBuffer;
 import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
 
 import java.util.List;
 
-/**
- * Used to represent a boolean
- */
+/** Used to represent a boolean */
 public class BooleanConstant implements Operation {
     private static final int OP_CODE = Operations.DATA_BOOLEAN;
-    boolean mValue = false;
+    private boolean mValue = false;
     private int mId;
 
     public BooleanConstant(int id, boolean value) {
@@ -39,15 +37,22 @@
         mValue = value;
     }
 
+    /**
+     * Get the value of the boolean constant
+     *
+     * @return the value of the boolean
+     */
+    public boolean getValue() {
+        return mValue;
+    }
+
     @Override
     public void write(WireBuffer buffer) {
         apply(buffer, mId, mValue);
     }
 
     @Override
-    public void apply(RemoteContext context) {
-
-    }
+    public void apply(RemoteContext context) {}
 
     @Override
     public String deepToString(String indent) {
@@ -88,14 +93,9 @@
     }
 
     public static void documentation(DocumentationBuilder doc) {
-        doc.operation("Expressions Operations",
-                        OP_CODE,
-                        "BooleanConstant")
+        doc.operation("Expressions Operations", OP_CODE, "BooleanConstant")
                 .description("A boolean and its associated id")
-                .field(INT, "id", "id of Int")
-                .field(BYTE, "value",
-                        "8-bit 0 or 1");
-
+                .field(DocumentedOperation.INT, "id", "id of Int")
+                .field(BYTE, "value", "8-bit 0 or 1");
     }
-
 }
diff --git a/core/java/com/android/internal/widget/remotecompose/core/types/IntegerConstant.java b/core/java/com/android/internal/widget/remotecompose/core/types/IntegerConstant.java
index 90faf52..3ef9db9 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/types/IntegerConstant.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/types/IntegerConstant.java
@@ -15,20 +15,18 @@
  */
 package com.android.internal.widget.remotecompose.core.types;
 
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
 
 import com.android.internal.widget.remotecompose.core.Operation;
 import com.android.internal.widget.remotecompose.core.Operations;
 import com.android.internal.widget.remotecompose.core.RemoteContext;
 import com.android.internal.widget.remotecompose.core.WireBuffer;
 import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
 
 import java.util.List;
 
-/**
- * Represents a single integer typically used for states
- * or named for input into the system
- */
+/** Represents a single integer typically used for states or named for input into the system */
 public class IntegerConstant implements Operation {
     private int mValue = 0;
     private int mId;
@@ -87,13 +85,9 @@
     }
 
     public static void documentation(DocumentationBuilder doc) {
-        doc.operation("Expressions Operations",
-                        id(),
-                        "IntegerConstant")
+        doc.operation("Expressions Operations", id(), "IntegerConstant")
                 .description("A integer and its associated id")
-                .field(INT, "id", "id of Int")
-                .field(INT, "value",
-                        "32-bit int value");
-
+                .field(DocumentedOperation.INT, "id", "id of Int")
+                .field(INT, "value", "32-bit int value");
     }
 }
diff --git a/core/java/com/android/internal/widget/remotecompose/core/types/LongConstant.java b/core/java/com/android/internal/widget/remotecompose/core/types/LongConstant.java
index 70402cf..6d51d19 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/types/LongConstant.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/types/LongConstant.java
@@ -15,23 +15,21 @@
  */
 package com.android.internal.widget.remotecompose.core.types;
 
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.LONG;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.LONG;
 
 import com.android.internal.widget.remotecompose.core.Operation;
 import com.android.internal.widget.remotecompose.core.Operations;
 import com.android.internal.widget.remotecompose.core.RemoteContext;
 import com.android.internal.widget.remotecompose.core.WireBuffer;
 import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
 
 import java.util.List;
 
-/**
- * Used to represent a long
- */
+/** Used to represent a long */
 public class LongConstant implements Operation {
     private static final int OP_CODE = Operations.DATA_LONG;
-    long mValue;
+    private long mValue;
     private int mId;
 
     public LongConstant(int id, long value) {
@@ -39,6 +37,15 @@
         mValue = value;
     }
 
+    /**
+     * Get the value of the long constant
+     *
+     * @return the value of the long
+     */
+    public long getValue() {
+        return mValue;
+    }
+
     @Override
     public void write(WireBuffer buffer) {
         apply(buffer, mId, mValue);
@@ -46,6 +53,7 @@
 
     @Override
     public void apply(RemoteContext context) {
+        context.putObject(mId, this);
     }
 
     @Override
@@ -79,14 +87,9 @@
     }
 
     public static void documentation(DocumentationBuilder doc) {
-        doc.operation("Expressions Operations",
-                        OP_CODE,
-                        "LongConstant")
+        doc.operation("Expressions Operations", OP_CODE, "LongConstant")
                 .description("A boolean and its associated id")
-                .field(INT, "id", "id of Int")
-                .field(LONG, "value",
-                        "The long Value");
-
+                .field(DocumentedOperation.INT, "id", "id of Int")
+                .field(LONG, "value", "The long Value");
     }
-
 }
diff --git a/core/java/com/android/internal/widget/remotecompose/player/RemoteComposeDocument.java b/core/java/com/android/internal/widget/remotecompose/player/RemoteComposeDocument.java
index e32f823..906282c 100644
--- a/core/java/com/android/internal/widget/remotecompose/player/RemoteComposeDocument.java
+++ b/core/java/com/android/internal/widget/remotecompose/player/RemoteComposeDocument.java
@@ -22,9 +22,7 @@
 
 import java.io.InputStream;
 
-/**
- * Public API to create a new RemoteComposeDocument coming from an input stream
- */
+/** Public API to create a new RemoteComposeDocument coming from an input stream */
 public class RemoteComposeDocument {
 
     CoreDocument mDocument = new CoreDocument();
@@ -48,23 +46,19 @@
     }
 
     /**
-     * Called when an initialization is needed, allowing the document to eg load
-     * resources / cache them.
+     * Called when an initialization is needed, allowing the document to eg load resources / cache
+     * them.
      */
     public void initializeContext(RemoteContext context) {
         mDocument.initializeContext(context);
     }
 
-    /**
-     * Returns the width of the document in pixels
-     */
+    /** Returns the width of the document in pixels */
     public int getWidth() {
         return mDocument.getWidth();
     }
 
-    /**
-     * Returns the height of the document in pixels
-     */
+    /** Returns the height of the document in pixels */
     public int getHeight() {
         return mDocument.getHeight();
     }
@@ -77,7 +71,7 @@
      * Paint the document
      *
      * @param context the provided PaintContext
-     * @param theme   the theme we want to use for this document.
+     * @param theme the theme we want to use for this document.
      */
     public void paint(RemoteContext context, int theme) {
         mDocument.paint(context, theme);
@@ -105,8 +99,7 @@
 
     @Override
     public String toString() {
-        return "Document{\n"
-                + mDocument + '}';
+        return "Document{\n" + mDocument + '}';
     }
 
     /**
@@ -120,6 +113,7 @@
 
     /**
      * Return a component associated with id
+     *
      * @param id the component id
      * @return the corresponding component or null if not found
      */
@@ -138,4 +132,3 @@
         return mDocument.getStats();
     }
 }
-
diff --git a/core/java/com/android/internal/widget/remotecompose/player/RemoteComposePlayer.java b/core/java/com/android/internal/widget/remotecompose/player/RemoteComposePlayer.java
index 3d78680..06bf4cd 100644
--- a/core/java/com/android/internal/widget/remotecompose/player/RemoteComposePlayer.java
+++ b/core/java/com/android/internal/widget/remotecompose/player/RemoteComposePlayer.java
@@ -29,9 +29,7 @@
 import com.android.internal.widget.remotecompose.core.operations.RootContentBehavior;
 import com.android.internal.widget.remotecompose.player.platform.RemoteComposeCanvas;
 
-/**
- * A view to to display and play RemoteCompose documents
- */
+/** A view to to display and play RemoteCompose documents */
 public class RemoteComposePlayer extends FrameLayout {
     private RemoteComposeCanvas mInner;
 
@@ -73,10 +71,7 @@
     public void setDocument(RemoteComposeDocument value) {
         if (value != null) {
             if (value.canBeDisplayed(
-                    MAX_SUPPORTED_MAJOR_VERSION,
-                    MAX_SUPPORTED_MINOR_VERSION, 0L
-            )
-            ) {
+                    MAX_SUPPORTED_MAJOR_VERSION, MAX_SUPPORTED_MINOR_VERSION, 0L)) {
                 mInner.setDocument(value);
                 int contentBehavior = value.getDocument().getContentScroll();
                 applyContentBehavior(contentBehavior);
@@ -90,65 +85,58 @@
     }
 
     /**
-     * Apply the content behavior (NONE|SCROLL_HORIZONTAL|SCROLL_VERTICAL) to the player,
-     * adding or removing scrollviews as needed.
+     * Apply the content behavior (NONE|SCROLL_HORIZONTAL|SCROLL_VERTICAL) to the player, adding or
+     * removing scrollviews as needed.
      *
      * @param contentBehavior document content behavior (NONE|SCROLL_HORIZONTAL|SCROLL_VERTICAL)
      */
     private void applyContentBehavior(int contentBehavior) {
         switch (contentBehavior) {
-            case RootContentBehavior.SCROLL_HORIZONTAL: {
+            case RootContentBehavior.SCROLL_HORIZONTAL:
                 if (!(mInner.getParent() instanceof HorizontalScrollView)) {
                     ((ViewGroup) mInner.getParent()).removeView(mInner);
                     removeAllViews();
-                    LayoutParams layoutParamsInner = new LayoutParams(
-                            LayoutParams.WRAP_CONTENT,
-                            LayoutParams.MATCH_PARENT);
+                    LayoutParams layoutParamsInner =
+                            new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT);
                     HorizontalScrollView horizontalScrollView =
                             new HorizontalScrollView(getContext());
                     horizontalScrollView.setBackgroundColor(Color.TRANSPARENT);
                     horizontalScrollView.setFillViewport(true);
                     horizontalScrollView.addView(mInner, layoutParamsInner);
-                    LayoutParams layoutParams = new LayoutParams(
-                            LayoutParams.MATCH_PARENT,
-                            LayoutParams.MATCH_PARENT);
+                    LayoutParams layoutParams =
+                            new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
                     addView(horizontalScrollView, layoutParams);
                 }
-            }
-            break;
-            case RootContentBehavior.SCROLL_VERTICAL: {
+                break;
+            case RootContentBehavior.SCROLL_VERTICAL:
                 if (!(mInner.getParent() instanceof ScrollView)) {
                     ((ViewGroup) mInner.getParent()).removeView(mInner);
                     removeAllViews();
-                    LayoutParams layoutParamsInner = new LayoutParams(
-                            LayoutParams.MATCH_PARENT,
-                            LayoutParams.WRAP_CONTENT);
+                    LayoutParams layoutParamsInner =
+                            new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
                     ScrollView scrollView = new ScrollView(getContext());
                     scrollView.setBackgroundColor(Color.TRANSPARENT);
                     scrollView.setFillViewport(true);
                     scrollView.addView(mInner, layoutParamsInner);
-                    LayoutParams layoutParams = new LayoutParams(
-                            LayoutParams.MATCH_PARENT,
-                            LayoutParams.MATCH_PARENT);
+                    LayoutParams layoutParams =
+                            new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
                     addView(scrollView, layoutParams);
                 }
-            }
-            break;
+                break;
             default:
                 if (mInner.getParent() != this) {
                     ((ViewGroup) mInner.getParent()).removeView(mInner);
                     removeAllViews();
-                    LayoutParams layoutParams = new LayoutParams(
-                            LayoutParams.MATCH_PARENT,
-                            LayoutParams.MATCH_PARENT);
+                    LayoutParams layoutParams =
+                            new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
                     addView(mInner, layoutParams);
                 }
         }
     }
 
     private void init(Context context, AttributeSet attrs, int defStyleAttr) {
-        LayoutParams layoutParams = new LayoutParams(LayoutParams.MATCH_PARENT,
-                LayoutParams.MATCH_PARENT);
+        LayoutParams layoutParams =
+                new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
         setBackgroundColor(Color.TRANSPARENT);
         mInner = new RemoteComposeCanvas(context, attrs, defStyleAttr);
         mInner.setBackgroundColor(Color.TRANSPARENT);
@@ -241,24 +229,25 @@
      * Add a callback for handling click events on the document
      *
      * @param callback the callback lambda that will be used when a click is detected
-     *                 <p>
-     *                 The parameter of the callback are:
-     *                 id : the id of the clicked area
-     *                 metadata: a client provided unstructured string associated with that area
+     *     <p>The parameter of the callback are:
+     *     <ul>
+     *       <li>id : the id of the clicked area
+     *       <li>metadata: a client provided unstructured string associated with that area
+     *     </ul>
      */
     public void addClickListener(ClickCallbacks callback) {
         mInner.addClickListener((id, metadata) -> callback.click(id, metadata));
     }
 
     /**
-     * Set the playback theme for the document. This allows to filter operations in order
-     * to have the document adapt to the given theme. This method is intended to be used
-     * to support night/light themes (system or app level), not custom themes.
+     * Set the playback theme for the document. This allows to filter operations in order to have
+     * the document adapt to the given theme. This method is intended to be used to support
+     * night/light themes (system or app level), not custom themes.
      *
-     * @param theme the theme used for playing the document. Possible values for theme are:
-     *              - Theme.UNSPECIFIED -- all instructions in the document will be executed
-     *              - Theme.DARK -- only executed NON Light theme instructions
-     *              - Theme.LIGHT -- only executed NON Dark theme instructions
+     * @param theme the theme used for playing the document. Possible values for theme are: -
+     *     Theme.UNSPECIFIED -- all instructions in the document will be executed - Theme.DARK --
+     *     only executed NON Light theme instructions - Theme.LIGHT -- only executed NON Dark theme
+     *     instructions
      */
     public void setTheme(int theme) {
         if (mInner.getTheme() != theme) {
@@ -277,8 +266,7 @@
     }
 
     /**
-     * This sets a color based on its name. Overriding the color set in
-     * the document.
+     * This sets a color based on its name. Overriding the color set in the document.
      *
      * @param colorName Name of the color
      * @param colorValue The new color value
@@ -364,7 +352,7 @@
                 case "colorFocusedHighlight":
                     setRColor(s, android.R.attr.colorFocusedHighlight);
                     break;
-                case "colorForeground":   // General foreground color for views.
+                case "colorForeground": // General foreground color for views.
                     setRColor(s, android.R.attr.colorForeground);
                     break;
                 // Foreground color for inverse backgrounds.
@@ -483,15 +471,14 @@
     }
 
     private int getColorFromResource(int id) {
+
         TypedValue typedValue = new TypedValue();
-        try (TypedArray arr = getContext()
-                .getApplicationContext()
-                .obtainStyledAttributes(typedValue.data, new int[]{id})) {
+        try (TypedArray arr =
+                getContext()
+                        .getApplicationContext()
+                        .obtainStyledAttributes(typedValue.data, new int[] {id})) {
             int color = arr.getColor(0, -1);
             return color;
         }
     }
-
-
 }
-
diff --git a/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidPaintContext.java b/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidPaintContext.java
index 4416cf7..f59a0d3 100644
--- a/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidPaintContext.java
+++ b/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidPaintContext.java
@@ -43,8 +43,8 @@
 import java.util.List;
 
 /**
- * An implementation of PaintContext for the Android Canvas.
- * This is used to play the RemoteCompose operations on Android.
+ * An implementation of PaintContext for the Android Canvas. This is used to play the RemoteCompose
+ * operations on Android.
  */
 public class AndroidPaintContext extends PaintContext {
     Paint mPaint = new Paint();
@@ -83,37 +83,36 @@
     /**
      * Draw an image onto the canvas
      *
-     * @param imageId   the id of the image
-     * @param srcLeft   left coordinate of the source area
-     * @param srcTop    top coordinate of the source area
-     * @param srcRight  right coordinate of the source area
+     * @param imageId the id of the image
+     * @param srcLeft left coordinate of the source area
+     * @param srcTop top coordinate of the source area
+     * @param srcRight right coordinate of the source area
      * @param srcBottom bottom coordinate of the source area
-     * @param dstLeft   left coordinate of the destination area
-     * @param dstTop    top coordinate of the destination area
-     * @param dstRight  right coordinate of the destination area
+     * @param dstLeft left coordinate of the destination area
+     * @param dstTop top coordinate of the destination area
+     * @param dstRight right coordinate of the destination area
      * @param dstBottom bottom coordinate of the destination area
      */
-
     @Override
-    public void drawBitmap(int imageId,
-                           int srcLeft,
-                           int srcTop,
-                           int srcRight,
-                           int srcBottom,
-                           int dstLeft,
-                           int dstTop,
-                           int dstRight,
-                           int dstBottom,
-                           int cdId) {
+    public void drawBitmap(
+            int imageId,
+            int srcLeft,
+            int srcTop,
+            int srcRight,
+            int srcBottom,
+            int dstLeft,
+            int dstTop,
+            int dstRight,
+            int dstBottom,
+            int cdId) {
         AndroidRemoteContext androidContext = (AndroidRemoteContext) mContext;
         if (androidContext.mRemoteComposeState.containsId(imageId)) {
-            Bitmap bitmap = (Bitmap) androidContext.mRemoteComposeState
-                    .getFromId(imageId);
+            Bitmap bitmap = (Bitmap) androidContext.mRemoteComposeState.getFromId(imageId);
             mCanvas.drawBitmap(
                     bitmap,
                     new Rect(srcLeft, srcTop, srcRight, srcBottom),
-                    new Rect(dstLeft, dstTop, dstRight, dstBottom), mPaint
-            );
+                    new Rect(dstLeft, dstTop, dstRight, dstBottom),
+                    mPaint);
         }
     }
 
@@ -128,28 +127,23 @@
     }
 
     @Override
-    public void drawArc(float left,
-                        float top,
-                        float right,
-                        float bottom,
-                        float startAngle,
-                        float sweepAngle) {
-        mCanvas.drawArc(left, top, right, bottom, startAngle,
-                sweepAngle, true, mPaint);
+    public void drawArc(
+            float left, float top, float right, float bottom, float startAngle, float sweepAngle) {
+        mCanvas.drawArc(left, top, right, bottom, startAngle, sweepAngle, false, mPaint);
     }
 
     @Override
-    public void drawBitmap(int id,
-                           float left,
-                           float top,
-                           float right,
-                           float bottom) {
+    public void drawSector(
+            float left, float top, float right, float bottom, float startAngle, float sweepAngle) {
+        mCanvas.drawArc(left, top, right, bottom, startAngle, sweepAngle, true, mPaint);
+    }
+
+    @Override
+    public void drawBitmap(int id, float left, float top, float right, float bottom) {
         AndroidRemoteContext androidContext = (AndroidRemoteContext) mContext;
         if (androidContext.mRemoteComposeState.containsId(id)) {
-            Bitmap bitmap =
-                    (Bitmap) androidContext.mRemoteComposeState.getFromId(id);
-            Rect src = new Rect(0, 0,
-                    bitmap.getWidth(), bitmap.getHeight());
+            Bitmap bitmap = (Bitmap) androidContext.mRemoteComposeState.getFromId(id);
+            Rect src = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());
             RectF dst = new RectF(left, top, right, bottom);
             mCanvas.drawBitmap(bitmap, src, dst, mPaint);
         }
@@ -191,21 +185,13 @@
     }
 
     @Override
-    public void drawRoundRect(float left,
-                              float top,
-                              float right,
-                              float bottom,
-                              float radiusX,
-                              float radiusY) {
-        mCanvas.drawRoundRect(left, top, right, bottom,
-                radiusX, radiusY, mPaint);
+    public void drawRoundRect(
+            float left, float top, float right, float bottom, float radiusX, float radiusY) {
+        mCanvas.drawRoundRect(left, top, right, bottom, radiusX, radiusY, mPaint);
     }
 
     @Override
-    public void drawTextOnPath(int textId,
-                               int pathId,
-                               float hOffset,
-                               float vOffset) {
+    public void drawTextOnPath(int textId, int pathId, float hOffset, float vOffset) {
         mCanvas.drawTextOnPath(getText(textId), getPath(pathId, 0, 1), hOffset, vOffset, mPaint);
     }
 
@@ -237,14 +223,15 @@
     }
 
     @Override
-    public void drawTextRun(int textID,
-                            int start,
-                            int end,
-                            int contextStart,
-                            int contextEnd,
-                            float x,
-                            float y,
-                            boolean rtl) {
+    public void drawTextRun(
+            int textID,
+            int start,
+            int end,
+            int contextStart,
+            int contextEnd,
+            float x,
+            float y,
+            boolean rtl) {
 
         String textToPaint = getText(textID);
         if (textToPaint == null) {
@@ -262,11 +249,7 @@
     }
 
     @Override
-    public void drawTweenPath(int path1Id,
-                              int path2Id,
-                              float tween,
-                              float start,
-                              float end) {
+    public void drawTweenPath(int path1Id, int path2Id, float tween, float start, float end) {
         mCanvas.drawPath(getPath(path1Id, path2Id, tween, start, end), mPaint);
     }
 
@@ -381,224 +364,219 @@
     /**
      * This applies paint changes to the current paint
      *
-     * @param mPaintData the list change to the paint
+     * @param paintData the list change to the paint
      */
     @Override
-    public void applyPaint(PaintBundle mPaintData) {
-        mPaintData.applyPaintChange((PaintContext) this, new PaintChanges() {
-            @Override
-            public void setTextSize(float size) {
-                mPaint.setTextSize(size);
-            }
-
-            @Override
-            public void setTypeFace(int fontType, int weight, boolean italic) {
-                int[] type = new int[]{Typeface.NORMAL, Typeface.BOLD,
-                        Typeface.ITALIC, Typeface.BOLD_ITALIC};
-
-                switch (fontType) {
-                    case PaintBundle.FONT_TYPE_DEFAULT: {
-                        if (weight == 400 && !italic) { // for normal case
-                            mPaint.setTypeface(Typeface.DEFAULT);
-                        } else {
-                            mPaint.setTypeface(Typeface.create(Typeface.DEFAULT,
-                                    weight, italic));
-                        }
-                        break;
+    public void applyPaint(PaintBundle paintData) {
+        paintData.applyPaintChange(
+                (PaintContext) this,
+                new PaintChanges() {
+                    @Override
+                    public void setTextSize(float size) {
+                        mPaint.setTextSize(size);
                     }
-                    case PaintBundle.FONT_TYPE_SERIF: {
-                        if (weight == 400 && !italic) { // for normal case
-                            mPaint.setTypeface(Typeface.SERIF);
-                        } else {
-                            mPaint.setTypeface(Typeface.create(Typeface.SERIF,
-                                    weight, italic));
-                        }
-                        break;
-                    }
-                    case PaintBundle.FONT_TYPE_SANS_SERIF: {
-                        if (weight == 400 && !italic) { //  for normal case
-                            mPaint.setTypeface(Typeface.SANS_SERIF);
-                        } else {
-                            mPaint.setTypeface(
-                                    Typeface.create(Typeface.SANS_SERIF,
-                                            weight, italic));
-                        }
-                        break;
-                    }
-                    case PaintBundle.FONT_TYPE_MONOSPACE: {
-                        if (weight == 400 && !italic) { //  for normal case
-                            mPaint.setTypeface(Typeface.MONOSPACE);
-                        } else {
-                            mPaint.setTypeface(
-                                    Typeface.create(Typeface.MONOSPACE,
-                                            weight, italic));
-                        }
 
-                        break;
-                    }
-                }
+                    @Override
+                    public void setTypeFace(int fontType, int weight, boolean italic) {
+                        int[] type =
+                                new int[] {
+                                    Typeface.NORMAL,
+                                    Typeface.BOLD,
+                                    Typeface.ITALIC,
+                                    Typeface.BOLD_ITALIC
+                                };
 
-            }
+                        switch (fontType) {
+                            case PaintBundle.FONT_TYPE_DEFAULT:
+                                if (weight == 400 && !italic) { // for normal case
+                                    mPaint.setTypeface(Typeface.DEFAULT);
+                                } else {
+                                    mPaint.setTypeface(
+                                            Typeface.create(Typeface.DEFAULT, weight, italic));
+                                }
+                                break;
+                            case PaintBundle.FONT_TYPE_SERIF:
+                                if (weight == 400 && !italic) { // for normal case
+                                    mPaint.setTypeface(Typeface.SERIF);
+                                } else {
+                                    mPaint.setTypeface(
+                                            Typeface.create(Typeface.SERIF, weight, italic));
+                                }
+                                break;
+                            case PaintBundle.FONT_TYPE_SANS_SERIF:
+                                if (weight == 400 && !italic) { //  for normal case
+                                    mPaint.setTypeface(Typeface.SANS_SERIF);
+                                } else {
+                                    mPaint.setTypeface(
+                                            Typeface.create(Typeface.SANS_SERIF, weight, italic));
+                                }
+                                break;
+                            case PaintBundle.FONT_TYPE_MONOSPACE:
+                                if (weight == 400 && !italic) { //  for normal case
+                                    mPaint.setTypeface(Typeface.MONOSPACE);
+                                } else {
+                                    mPaint.setTypeface(
+                                            Typeface.create(Typeface.MONOSPACE, weight, italic));
+                                }
 
-            @Override
-            public void setStrokeWidth(float width) {
-                mPaint.setStrokeWidth(width);
-            }
-
-            @Override
-            public void setColor(int color) {
-                mPaint.setColor(color);
-            }
-
-            @Override
-            public void setStrokeCap(int cap) {
-                mPaint.setStrokeCap(Paint.Cap.values()[cap]);
-            }
-
-            @Override
-            public void setStyle(int style) {
-                mPaint.setStyle(Paint.Style.values()[style]);
-            }
-
-            @Override
-            public void setShader(int shaderId) {
-                // TODO this stuff should check the shader creation
-                if (shaderId == 0) {
-                    mPaint.setShader(null);
-                    return;
-                }
-                ShaderData data = getShaderData(shaderId);
-                RuntimeShader shader = new RuntimeShader(getText(data.getShaderTextId()));
-                String[] names = data.getUniformFloatNames();
-                for (int i = 0; i < names.length; i++) {
-                    String name = names[i];
-                    float[] val = data.getUniformFloats(name);
-                    shader.setFloatUniform(name, val);
-                }
-                names = data.getUniformIntegerNames();
-                for (int i = 0; i < names.length; i++) {
-                    String name = names[i];
-                    int[] val = data.getUniformInts(name);
-                    shader.setIntUniform(name, val);
-                }
-                names = data.getUniformBitmapNames();
-                for (int i = 0; i < names.length; i++) {
-                    String name = names[i];
-                    int val = data.getUniformBitmapId(name);
-                }
-                mPaint.setShader(shader);
-            }
-
-            @Override
-            public void setImageFilterQuality(int quality) {
-                Utils.log(" quality =" + quality);
-            }
-
-            @Override
-            public void setBlendMode(int mode) {
-                mPaint.setBlendMode(origamiToBlendMode(mode));
-            }
-
-            @Override
-            public void setAlpha(float a) {
-                mPaint.setAlpha((int) (255 * a));
-            }
-
-            @Override
-            public void setStrokeMiter(float miter) {
-                mPaint.setStrokeMiter(miter);
-            }
-
-            @Override
-            public void setStrokeJoin(int join) {
-                mPaint.setStrokeJoin(Paint.Join.values()[join]);
-            }
-
-            @Override
-            public void setFilterBitmap(boolean filter) {
-                mPaint.setFilterBitmap(filter);
-            }
-
-            @Override
-            public void setAntiAlias(boolean aa) {
-                mPaint.setAntiAlias(aa);
-            }
-
-            @Override
-            public void clear(long mask) {
-                if (true) return;
-                long m = mask;
-                int k = 1;
-                while (m > 0) {
-                    if ((m & 1) == 1L) {
-                        switch (k) {
-
-                            case PaintBundle.COLOR_FILTER:
-                                mPaint.setColorFilter(null);
                                 break;
                         }
                     }
-                    k++;
-                    m = m >> 1;
-                }
-            }
 
-            Shader.TileMode[] mTileModes = new Shader.TileMode[]{
-                    Shader.TileMode.CLAMP,
-                    Shader.TileMode.REPEAT,
-                    Shader.TileMode.MIRROR};
+                    @Override
+                    public void setStrokeWidth(float width) {
+                        mPaint.setStrokeWidth(width);
+                    }
 
-            @Override
-            public void setLinearGradient(int[] colors,
-                                          float[] stops,
-                                          float startX,
-                                          float startY,
-                                          float endX,
-                                          float endY,
-                                          int tileMode) {
-                mPaint.setShader(new LinearGradient(startX,
-                        startY,
-                        endX,
-                        endY, colors, stops, mTileModes[tileMode]));
+                    @Override
+                    public void setColor(int color) {
+                        mPaint.setColor(color);
+                    }
 
-            }
+                    @Override
+                    public void setStrokeCap(int cap) {
+                        mPaint.setStrokeCap(Paint.Cap.values()[cap]);
+                    }
 
-            @Override
-            public void setRadialGradient(int[] colors,
-                                          float[] stops,
-                                          float centerX,
-                                          float centerY,
-                                          float radius,
-                                          int tileMode) {
-                mPaint.setShader(new RadialGradient(centerX, centerY, radius,
-                        colors, stops, mTileModes[tileMode]));
-            }
+                    @Override
+                    public void setStyle(int style) {
+                        mPaint.setStyle(Paint.Style.values()[style]);
+                    }
 
-            @Override
-            public void setSweepGradient(int[] colors,
-                                         float[] stops,
-                                         float centerX,
-                                         float centerY) {
-                mPaint.setShader(new SweepGradient(centerX, centerY, colors, stops));
+                    @Override
+                    public void setShader(int shaderId) {
+                        // TODO this stuff should check the shader creation
+                        if (shaderId == 0) {
+                            mPaint.setShader(null);
+                            return;
+                        }
+                        ShaderData data = getShaderData(shaderId);
+                        RuntimeShader shader = new RuntimeShader(getText(data.getShaderTextId()));
+                        String[] names = data.getUniformFloatNames();
+                        for (int i = 0; i < names.length; i++) {
+                            String name = names[i];
+                            float[] val = data.getUniformFloats(name);
+                            shader.setFloatUniform(name, val);
+                        }
+                        names = data.getUniformIntegerNames();
+                        for (int i = 0; i < names.length; i++) {
+                            String name = names[i];
+                            int[] val = data.getUniformInts(name);
+                            shader.setIntUniform(name, val);
+                        }
+                        names = data.getUniformBitmapNames();
+                        for (int i = 0; i < names.length; i++) {
+                            String name = names[i];
+                            int val = data.getUniformBitmapId(name);
+                        }
+                        mPaint.setShader(shader);
+                    }
 
-            }
+                    @Override
+                    public void setImageFilterQuality(int quality) {
+                        Utils.log(" quality =" + quality);
+                    }
 
-            @Override
-            public void setColorFilter(int color, int mode) {
-                PorterDuff.Mode pmode = origamiToPorterDuffMode(mode);
-                if (pmode != null) {
-                    mPaint.setColorFilter(
-                            new PorterDuffColorFilter(color, pmode));
-                }
-            }
-        });
+                    @Override
+                    public void setBlendMode(int mode) {
+                        mPaint.setBlendMode(origamiToBlendMode(mode));
+                    }
+
+                    @Override
+                    public void setAlpha(float a) {
+                        mPaint.setAlpha((int) (255 * a));
+                    }
+
+                    @Override
+                    public void setStrokeMiter(float miter) {
+                        mPaint.setStrokeMiter(miter);
+                    }
+
+                    @Override
+                    public void setStrokeJoin(int join) {
+                        mPaint.setStrokeJoin(Paint.Join.values()[join]);
+                    }
+
+                    @Override
+                    public void setFilterBitmap(boolean filter) {
+                        mPaint.setFilterBitmap(filter);
+                    }
+
+                    @Override
+                    public void setAntiAlias(boolean aa) {
+                        mPaint.setAntiAlias(aa);
+                    }
+
+                    @Override
+                    public void clear(long mask) {
+                        if ((mask & (1L << PaintBundle.COLOR_FILTER)) != 0) {
+                            mPaint.setColorFilter(null);
+                        }
+                    }
+
+                    Shader.TileMode[] mTileModes =
+                            new Shader.TileMode[] {
+                                Shader.TileMode.CLAMP,
+                                Shader.TileMode.REPEAT,
+                                Shader.TileMode.MIRROR
+                            };
+
+                    @Override
+                    public void setLinearGradient(
+                            int[] colors,
+                            float[] stops,
+                            float startX,
+                            float startY,
+                            float endX,
+                            float endY,
+                            int tileMode) {
+                        mPaint.setShader(
+                                new LinearGradient(
+                                        startX,
+                                        startY,
+                                        endX,
+                                        endY,
+                                        colors,
+                                        stops,
+                                        mTileModes[tileMode]));
+                    }
+
+                    @Override
+                    public void setRadialGradient(
+                            int[] colors,
+                            float[] stops,
+                            float centerX,
+                            float centerY,
+                            float radius,
+                            int tileMode) {
+                        mPaint.setShader(
+                                new RadialGradient(
+                                        centerX,
+                                        centerY,
+                                        radius,
+                                        colors,
+                                        stops,
+                                        mTileModes[tileMode]));
+                    }
+
+                    @Override
+                    public void setSweepGradient(
+                            int[] colors, float[] stops, float centerX, float centerY) {
+                        mPaint.setShader(new SweepGradient(centerX, centerY, colors, stops));
+                    }
+
+                    @Override
+                    public void setColorFilter(int color, int mode) {
+                        PorterDuff.Mode pmode = origamiToPorterDuffMode(mode);
+                        if (pmode != null) {
+                            mPaint.setColorFilter(new PorterDuffColorFilter(color, pmode));
+                        }
+                    }
+                });
     }
 
     @Override
-    public void matrixScale(float scaleX,
-                            float scaleY,
-                            float centerX,
-                            float centerY) {
+    public void matrixScale(float scaleX, float scaleY, float centerX, float centerY) {
         if (Float.isNaN(centerX)) {
             mCanvas.scale(scaleX, scaleY);
         } else {
@@ -622,7 +600,6 @@
             mCanvas.rotate(rotate);
         } else {
             mCanvas.rotate(rotate, pivotX, pivotY);
-
         }
     }
 
@@ -642,15 +619,27 @@
     }
 
     @Override
-    public void roundedClipRect(float width, float height,
-                                float topStart, float topEnd,
-                                float bottomStart, float bottomEnd) {
+    public void roundedClipRect(
+            float width,
+            float height,
+            float topStart,
+            float topEnd,
+            float bottomStart,
+            float bottomEnd) {
         Path roundedPath = new Path();
-        float[] radii = new float[] { topStart, topStart,
-                topEnd, topEnd, bottomEnd, bottomEnd, bottomStart, bottomStart};
+        float[] radii =
+                new float[] {
+                    topStart,
+                    topStart,
+                    topEnd,
+                    topEnd,
+                    bottomEnd,
+                    bottomEnd,
+                    bottomStart,
+                    bottomStart
+                };
 
-        roundedPath.addRoundRect(0f, 0f, width, height,
-                radii, android.graphics.Path.Direction.CW);
+        roundedPath.addRoundRect(0f, 0f, width, height, radii, android.graphics.Path.Direction.CW);
         mCanvas.clipPath(roundedPath);
     }
 
@@ -660,7 +649,7 @@
         if (regionOp == ClipPath.DIFFERENCE) {
             mCanvas.clipOutPath(path); // DIFFERENCE
         } else {
-            mCanvas.clipPath(path);  // INTERSECT
+            mCanvas.clipPath(path); // INTERSECT
         }
     }
 
@@ -669,11 +658,7 @@
         mPaint.reset();
     }
 
-    private Path getPath(int path1Id,
-                         int path2Id,
-                         float tween,
-                         float start,
-                         float end) {
+    private Path getPath(int path1Id, int path2Id, float tween, float start, float end) {
         if (tween == 0.0f) {
             return getPath(path1Id, start, end);
         }
@@ -681,10 +666,8 @@
             return getPath(path2Id, start, end);
         }
         AndroidRemoteContext androidContext = (AndroidRemoteContext) mContext;
-        float[] data1 =
-                (float[]) androidContext.mRemoteComposeState.getFromId(path1Id);
-        float[] data2 =
-                (float[]) androidContext.mRemoteComposeState.getFromId(path2Id);
+        float[] data1 = (float[]) androidContext.mRemoteComposeState.getFromId(path1Id);
+        float[] data2 = (float[]) androidContext.mRemoteComposeState.getFromId(path2Id);
         float[] tmp = new float[data2.length];
         for (int i = 0; i < tmp.length; i++) {
             if (Float.isNaN(data1[i]) || Float.isNaN(data2[i])) {
@@ -702,8 +685,7 @@
         AndroidRemoteContext androidContext = (AndroidRemoteContext) mContext;
         Path path = new Path();
         if (androidContext.mRemoteComposeState.containsId(id)) {
-            float[] data =
-                    (float[]) androidContext.mRemoteComposeState.getFromId(id);
+            float[] data = (float[]) androidContext.mRemoteComposeState.getFromId(id);
             FloatsToPath.genPath(path, data, start, end);
         }
         return path;
@@ -717,4 +699,3 @@
         return (ShaderData) mContext.mRemoteComposeState.getFromId(id);
     }
 }
-
diff --git a/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidPlatformServices.java b/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidPlatformServices.java
new file mode 100644
index 0000000..f9b22a2
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidPlatformServices.java
@@ -0,0 +1,119 @@
+/*
+ * 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.internal.widget.remotecompose.player.platform;
+
+import android.graphics.Bitmap;
+import android.graphics.Path;
+import android.graphics.PathIterator;
+
+import com.android.internal.widget.remotecompose.core.Platform;
+import com.android.internal.widget.remotecompose.core.operations.PathData;
+
+import java.io.ByteArrayOutputStream;
+import java.util.Arrays;
+
+/** Services that are needed to be provided by the platform during encoding. */
+public class AndroidPlatformServices implements Platform {
+    @Override
+    public byte[] imageToByteArray(Object image) {
+        if (image instanceof Bitmap) {
+            // let's create a bitmap
+            ByteArrayOutputStream byteArrayBitmapStream = new ByteArrayOutputStream();
+            ((Bitmap) image).compress(Bitmap.CompressFormat.PNG, 90, byteArrayBitmapStream);
+            return byteArrayBitmapStream.toByteArray();
+        }
+        return null;
+    }
+
+    @Override
+    public int getImageWidth(Object image) {
+        if (image instanceof Bitmap) {
+            return ((Bitmap) image).getWidth();
+        }
+        return 0;
+    }
+
+    @Override
+    public int getImageHeight(Object image) {
+        if (image instanceof Bitmap) {
+            return ((Bitmap) image).getHeight();
+        }
+        return 0;
+    }
+
+    @Override
+    public float[] pathToFloatArray(Object path) {
+        //        if (path is RemotePath) {
+        //            return path.createFloatArray()
+        //        }
+
+        if (path instanceof Path) {
+            return androidPathToFloatArray((Path) path);
+        }
+
+        return null;
+    }
+
+    private float[] androidPathToFloatArray(Path path) {
+        PathIterator i = path.getPathIterator();
+        int estimatedSize = 0;
+
+        while (i.hasNext()) {
+            i.next();
+            estimatedSize++;
+        }
+
+        PathIterator iter = path.getPathIterator();
+        float[] pathFloat = new float[estimatedSize * 10];
+
+        int count = 0;
+        while (i.hasNext()) {
+            PathIterator.Segment seg = i.next();
+
+            switch (seg.getVerb()) {
+                case PathIterator.VERB_MOVE:
+                    pathFloat[count++] = PathData.MOVE_NAN;
+                    break;
+                case PathIterator.VERB_LINE:
+                    pathFloat[count++] = PathData.LINE_NAN;
+                    break;
+                case PathIterator.VERB_QUAD:
+                    pathFloat[count++] = PathData.QUADRATIC_NAN;
+                    break;
+                case PathIterator.VERB_CONIC:
+                    pathFloat[count++] = PathData.CONIC_NAN;
+                    break;
+                case PathIterator.VERB_CUBIC:
+                    pathFloat[count++] = PathData.CUBIC_NAN;
+                    break;
+                case PathIterator.VERB_CLOSE:
+                    pathFloat[count++] = PathData.CLOSE_NAN;
+                    break;
+                case PathIterator.VERB_DONE:
+                    pathFloat[count++] = PathData.DONE_NAN;
+                    break;
+            }
+            for (float p : seg.getPoints()) {
+                pathFloat[count++] = p;
+            }
+            if (seg.getVerb() == PathIterator.VERB_CONIC) {
+                pathFloat[count++] = seg.getConicWeight();
+            }
+        }
+
+        return Arrays.copyOf(pathFloat, count);
+    }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidRemoteContext.java b/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidRemoteContext.java
index c989375..e7c0cc8 100644
--- a/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidRemoteContext.java
+++ b/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidRemoteContext.java
@@ -24,13 +24,14 @@
 import com.android.internal.widget.remotecompose.core.operations.FloatExpression;
 import com.android.internal.widget.remotecompose.core.operations.ShaderData;
 import com.android.internal.widget.remotecompose.core.operations.utilities.ArrayAccess;
+import com.android.internal.widget.remotecompose.core.operations.utilities.DataMap;
 
 import java.util.HashMap;
 
 /**
  * An implementation of Context for Android.
- * <p>
- * This is used to play the RemoteCompose operations on Android.
+ *
+ * <p>This is used to play the RemoteCompose operations on Android.
  */
 class AndroidRemoteContext extends RemoteContext {
 
@@ -127,6 +128,16 @@
     }
 
     @Override
+    public void putDataMap(int id, DataMap map) {
+        mRemoteComposeState.putDataMap(id, map);
+    }
+
+    @Override
+    public DataMap getDataMap(int id) {
+        return mRemoteComposeState.getDataMap(id);
+    }
+
+    @Override
     public void runAction(int id, String metadata) {
         mDocument.performClick(id);
     }
@@ -140,7 +151,7 @@
     /**
      * Decode a byte array into an image and cache it using the given imageId
      *
-     * @param width  with of image to be loaded
+     * @param width with of image to be loaded
      * @param height height of image to be loaded
      * @param bitmap a byte array containing the image information
      * @oaram imageId the id of the image
@@ -223,8 +234,18 @@
     }
 
     @Override
+    public void putObject(int id, Object value) {
+        mRemoteComposeState.updateObject(id, value);
+    }
+
+    @Override
+    public Object getObject(int id) {
+        return mRemoteComposeState.getObject(id);
+    }
+
+    @Override
     public int getInteger(int id) {
-        return  mRemoteComposeState.getInteger(id);
+        return mRemoteComposeState.getInteger(id);
     }
 
     @Override
@@ -252,17 +273,16 @@
     ///////////////////////////////////////////////////////////////////////////////////////////////
 
     @Override
-    public void addClickArea(int id,
-                             int contentDescriptionId,
-                             float left,
-                             float top,
-                             float right,
-                             float bottom,
-                             int metadataId) {
+    public void addClickArea(
+            int id,
+            int contentDescriptionId,
+            float left,
+            float top,
+            float right,
+            float bottom,
+            int metadataId) {
         String contentDescription = (String) mRemoteComposeState.getFromId(contentDescriptionId);
         String metadata = (String) mRemoteComposeState.getFromId(metadataId);
         mDocument.addClickArea(id, contentDescription, left, top, right, bottom, metadata);
     }
-
 }
-
diff --git a/core/java/com/android/internal/widget/remotecompose/player/platform/ClickAreaView.java b/core/java/com/android/internal/widget/remotecompose/player/platform/ClickAreaView.java
index 329178a..fdd9aad 100644
--- a/core/java/com/android/internal/widget/remotecompose/player/platform/ClickAreaView.java
+++ b/core/java/com/android/internal/widget/remotecompose/player/platform/ClickAreaView.java
@@ -20,9 +20,7 @@
 import android.graphics.Paint;
 import android.view.View;
 
-/**
- * Implementation for the click handling
- */
+/** Implementation for the click handling */
 class ClickAreaView extends View {
     private int mId;
     private String mMetadata;
@@ -30,8 +28,8 @@
 
     private boolean mDebug;
 
-    ClickAreaView(Context context, boolean debug, int id,
-                         String contentDescription, String metadata) {
+    ClickAreaView(
+            Context context, boolean debug, int id, String contentDescription, String metadata) {
         super(context);
         this.mId = id;
         this.mMetadata = metadata;
diff --git a/core/java/com/android/internal/widget/remotecompose/player/platform/FloatsToPath.java b/core/java/com/android/internal/widget/remotecompose/player/platform/FloatsToPath.java
index d75232a..8d3440c 100644
--- a/core/java/com/android/internal/widget/remotecompose/player/platform/FloatsToPath.java
+++ b/core/java/com/android/internal/widget/remotecompose/player/platform/FloatsToPath.java
@@ -23,72 +23,56 @@
 import com.android.internal.widget.remotecompose.core.operations.PathData;
 
 public class FloatsToPath {
-    public static void genPath(Path retPath,
-                               float[] floatPath,
-                               float start,
-                               float stop) {
+    public static void genPath(Path retPath, float[] floatPath, float start, float stop) {
         int i = 0;
         Path path = new Path(); // todo this should be cached for performance
         while (i < floatPath.length) {
             switch (idFromNan(floatPath[i])) {
-                case PathData.MOVE: {
+                case PathData.MOVE:
                     i++;
                     path.moveTo(floatPath[i + 0], floatPath[i + 1]);
                     i += 2;
-                }
-                break;
-                case PathData.LINE: {
+                    break;
+                case PathData.LINE:
                     i += 3;
                     path.lineTo(floatPath[i + 0], floatPath[i + 1]);
                     i += 2;
-                }
-                break;
-                case PathData.QUADRATIC: {
+                    break;
+                case PathData.QUADRATIC:
                     i += 3;
                     path.quadTo(
+                            floatPath[i + 0], floatPath[i + 1], floatPath[i + 2], floatPath[i + 3]);
+                    i += 4;
+                    break;
+                case PathData.CONIC:
+                    i += 3;
+
+                    path.conicTo(
                             floatPath[i + 0],
                             floatPath[i + 1],
                             floatPath[i + 2],
-                            floatPath[i + 3]
-                    );
-                    i += 4;
+                            floatPath[i + 3],
+                            floatPath[i + 4]);
 
-                }
-                break;
-                case PathData.CONIC: {
-                    i += 3;
-                    path.conicTo(
-                            floatPath[i + 0], floatPath[i + 1],
-                            floatPath[i + 2], floatPath[i + 3],
-                            floatPath[i + 4]
-                    );
                     i += 5;
-                }
-                break;
-                case PathData.CUBIC: {
+                    break;
+                case PathData.CUBIC:
                     i += 3;
                     path.cubicTo(
                             floatPath[i + 0], floatPath[i + 1],
                             floatPath[i + 2], floatPath[i + 3],
-                            floatPath[i + 4], floatPath[i + 5]
-                    );
+                            floatPath[i + 4], floatPath[i + 5]);
                     i += 6;
-                }
-                break;
-                case PathData.CLOSE: {
-
+                    break;
+                case PathData.CLOSE:
                     path.close();
                     i++;
-                }
-                break;
-                case PathData.DONE: {
+                    break;
+                case PathData.DONE:
                     i++;
-                }
-                break;
-                default: {
-                    System.err.println(" Odd command "
-                            + idFromNan(floatPath[i]));
-                }
+                    break;
+                default:
+                    System.err.println(" Odd command " + idFromNan(floatPath[i]));
             }
         }
 
@@ -101,8 +85,7 @@
                 float len = measure.getLength();
                 float scaleStart = Math.max(start, 0f) * len;
                 float scaleStop = Math.min(stop, 1f) * len;
-                measure.getSegment(scaleStart, scaleStop, retPath,
-                        true);
+                measure.getSegment(scaleStart, scaleStop, retPath, true);
             }
         } else {
 
diff --git a/core/java/com/android/internal/widget/remotecompose/player/platform/RemoteComposeCanvas.java b/core/java/com/android/internal/widget/remotecompose/player/platform/RemoteComposeCanvas.java
index f91e158..7de6988 100644
--- a/core/java/com/android/internal/widget/remotecompose/player/platform/RemoteComposeCanvas.java
+++ b/core/java/com/android/internal/widget/remotecompose/player/platform/RemoteComposeCanvas.java
@@ -31,9 +31,7 @@
 
 import java.util.Set;
 
-/**
- * Internal view handling the actual painting / interactions
- */
+/** Internal view handling the actual painting / interactions */
 public class RemoteComposeCanvas extends FrameLayout implements View.OnAttachStateChangeListener {
 
     static final boolean USE_VIEW_AREA_CLICK = true; // Use views to represent click areas
@@ -102,13 +100,17 @@
     private void updateClickAreas() {
         if (USE_VIEW_AREA_CLICK && mDocument != null) {
             mHasClickAreas = false;
-            Set<CoreDocument.ClickAreaRepresentation> clickAreas = mDocument
-                    .getDocument().getClickAreas();
+            Set<CoreDocument.ClickAreaRepresentation> clickAreas =
+                    mDocument.getDocument().getClickAreas();
             removeAllViews();
             for (CoreDocument.ClickAreaRepresentation area : clickAreas) {
-                ClickAreaView viewArea = new ClickAreaView(getContext(), mDebug,
-                        area.getId(), area.getContentDescription(),
-                        area.getMetadata());
+                ClickAreaView viewArea =
+                        new ClickAreaView(
+                                getContext(),
+                                mDebug,
+                                area.getId(),
+                                area.getContentDescription(),
+                                area.getMetadata());
                 int w = (int) area.width();
                 int h = (int) area.height();
                 FrameLayout.LayoutParams param = new FrameLayout.LayoutParams(w, h);
@@ -116,8 +118,8 @@
                 param.height = h;
                 param.leftMargin = (int) area.getLeft();
                 param.topMargin = (int) area.getTop();
-                viewArea.setOnClickListener(view1
-                        -> mDocument.getDocument().performClick(area.getId()));
+                viewArea.setOnClickListener(
+                        view1 -> mDocument.getDocument().performClick(area.getId()));
                 addView(viewArea, param);
             }
             if (!clickAreas.isEmpty()) {
@@ -201,23 +203,19 @@
             return super.onTouchEvent(event);
         }
         switch (event.getActionMasked()) {
-            case MotionEvent.ACTION_DOWN: {
+            case MotionEvent.ACTION_DOWN:
                 mActionDownPoint.x = (int) event.getX();
                 mActionDownPoint.y = (int) event.getY();
                 mInActionDown = true;
                 return true;
-            }
-            case MotionEvent.ACTION_CANCEL: {
+            case MotionEvent.ACTION_CANCEL:
                 mInActionDown = false;
                 return true;
-            }
-            case MotionEvent.ACTION_UP: {
+            case MotionEvent.ACTION_UP:
                 mInActionDown = false;
                 performClick();
                 return true;
-            }
-            case MotionEvent.ACTION_MOVE: {
-            }
+            case MotionEvent.ACTION_MOVE:
         }
         return false;
     }
@@ -227,8 +225,9 @@
         if (USE_VIEW_AREA_CLICK && mHasClickAreas) {
             return super.performClick();
         }
-        mDocument.getDocument().onClick(mARContext,
-                 (float) mActionDownPoint.x, (float) mActionDownPoint.y);
+        mDocument
+                .getDocument()
+                .onClick(mARContext, (float) mActionDownPoint.x, (float) mActionDownPoint.y);
         super.performClick();
         invalidate();
         return true;
@@ -305,6 +304,4 @@
             invalidate();
         }
     }
-
 }
-
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 816ace2..2283b88 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -73,10 +73,12 @@
 
     srcs: [
         "android_animation_PropertyValuesHolder.cpp",
+        "android_content_res_ApkAssets.cpp",
         "android_os_SystemClock.cpp",
         "android_os_SystemProperties.cpp",
         "android_os_Trace.cpp",
         "android_text_AndroidCharacter.cpp",
+        "android_util_AssetManager.cpp",
         "android_util_EventLog.cpp",
         "android_util_Log.cpp",
         "android_util_StringBlock.cpp",
@@ -203,7 +205,6 @@
                 "android_os_incremental_IncrementalManager.cpp",
                 "android_net_LocalSocketImpl.cpp",
                 "android_service_DataLoaderService.cpp",
-                "android_util_AssetManager.cpp",
                 "android_util_Binder.cpp",
                 "android_util_CharsetUtils.cpp",
                 "android_util_MemoryIntArray.cpp",
@@ -249,8 +250,8 @@
                 "android_backup_BackupDataOutput.cpp",
                 "android_backup_FileBackupHelperBase.cpp",
                 "android_backup_BackupHelperDispatcher.cpp",
+                "android_app_PropertyInvalidatedCache.cpp",
                 "android_app_backup_FullBackup.cpp",
-                "android_content_res_ApkAssets.cpp",
                 "android_content_res_ObbScanner.cpp",
                 "android_content_res_Configuration.cpp",
                 "android_content_res_ResourceTimer.cpp",
@@ -442,7 +443,6 @@
         },
         host_linux: {
             srcs: [
-                "android_content_res_ApkAssets.cpp",
                 "android_database_CursorWindow.cpp",
                 "android_database_SQLiteCommon.cpp",
                 "android_database_SQLiteConnection.cpp",
@@ -457,7 +457,6 @@
                 "android_view_InputEventReceiver.cpp",
                 "android_view_InputEventSender.cpp",
 
-                "android_util_AssetManager.cpp",
                 "android_util_Binder.cpp",
 
                 "android_util_FileObserver.cpp",
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 76f66cd..821861e 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -177,6 +177,7 @@
 extern int register_android_app_Activity(JNIEnv *env);
 extern int register_android_app_ActivityThread(JNIEnv *env);
 extern int register_android_app_NativeActivity(JNIEnv *env);
+extern int register_android_app_PropertyInvalidatedCache(JNIEnv* env);
 extern int register_android_media_RemoteDisplay(JNIEnv *env);
 extern int register_android_util_jar_StrictJarFile(JNIEnv* env);
 extern int register_android_view_InputChannel(JNIEnv* env);
@@ -1659,6 +1660,7 @@
         REG_JNI(register_android_app_Activity),
         REG_JNI(register_android_app_ActivityThread),
         REG_JNI(register_android_app_NativeActivity),
+        REG_JNI(register_android_app_PropertyInvalidatedCache),
         REG_JNI(register_android_util_jar_StrictJarFile),
         REG_JNI(register_android_view_InputChannel),
         REG_JNI(register_android_view_InputEventReceiver),
diff --git a/core/jni/android_app_PropertyInvalidatedCache.cpp b/core/jni/android_app_PropertyInvalidatedCache.cpp
new file mode 100644
index 0000000..ead6666
--- /dev/null
+++ b/core/jni/android_app_PropertyInvalidatedCache.cpp
@@ -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.
+ */
+
+#define LOG_TAG "CacheNonce"
+
+#include <string.h>
+#include <memory.h>
+
+#include <atomic>
+
+#include <nativehelper/JNIHelp.h>
+#include <nativehelper/scoped_primitive_array.h>
+#include <android-base/logging.h>
+
+#include "core_jni_helpers.h"
+#include "android_app_PropertyInvalidatedCache.h"
+
+namespace {
+
+using namespace android::app::PropertyInvalidatedCache;
+
+// Convert a jlong to a nonce block.  This is a convenience function that should be inlined by
+// the compiler.
+inline SystemCacheNonce* sysCache(jlong ptr) {
+    return reinterpret_cast<SystemCacheNonce*>(ptr);
+}
+
+// Return the number of nonces in the nonce block.
+jint getMaxNonce(JNIEnv*, jclass, jlong ptr) {
+    return sysCache(ptr)->getMaxNonce();
+}
+
+// Return the number of string bytes in the nonce block.
+jint getMaxByte(JNIEnv*, jclass, jlong ptr) {
+    return sysCache(ptr)->getMaxByte();
+}
+
+// Set the byte block.  The first int is the hash to set and the second is the array to copy.
+// This should be synchronized in the Java layer.
+void setByteBlock(JNIEnv* env, jclass, jlong ptr, jint hash, jbyteArray val) {
+    ScopedByteArrayRO value(env, val);
+    if (value.get() == nullptr) {
+        jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", "null byte block");
+        return;
+    }
+    sysCache(ptr)->setByteBlock(hash, value.get(), value.size());
+}
+
+// Fetch the byte block.  If the incoming hash is the same as the local hash, the Java layer is
+// presumed to have an up-to-date copy of the byte block; do not copy byte array.  The local
+// hash is returned.
+jint getByteBlock(JNIEnv* env, jclass, jlong ptr, jint hash, jbyteArray val) {
+    if (sysCache(ptr)->getHash() == hash) {
+        return hash;
+    }
+    ScopedByteArrayRW value(env, val);
+    return sysCache(ptr)->getByteBlock(value.get(), value.size());
+}
+
+// Fetch the byte block hash.
+//
+// This is a CriticalNative method and therefore does not get the JNIEnv or jclass parameters.
+jint getByteBlockHash(jlong ptr) {
+    return sysCache(ptr)->getHash();
+}
+
+// Get a nonce value. So that this method can be CriticalNative, it returns 0 if the value is
+// out of range, rather than throwing an exception.  This is a CriticalNative method and
+// therefore does not get the JNIEnv or jclass parameters.
+//
+// This method is @CriticalNative and does not take a JNIEnv* or jclass argument.
+jlong getNonce(jlong ptr, jint index) {
+    return sysCache(ptr)->getNonce(index);
+}
+
+// Set a nonce value. So that this method can be CriticalNative, it returns a boolean: false if
+// the index is out of range and true otherwise.  Callers may test the returned boolean and
+// generate an exception.
+//
+// This method is @CriticalNative and does not take a JNIEnv* or jclass argument.
+jboolean setNonce(jlong ptr, jint index, jlong value) {
+    return sysCache(ptr)->setNonce(index, value);
+}
+
+static const JNINativeMethod gMethods[] = {
+    {"nativeGetMaxNonce",      "(J)I",    (void*) getMaxNonce },
+    {"nativeGetMaxByte",       "(J)I",    (void*) getMaxByte },
+    {"nativeSetByteBlock",     "(JI[B)V", (void*) setByteBlock },
+    {"nativeGetByteBlock",     "(JI[B)I", (void*) getByteBlock },
+    {"nativeGetByteBlockHash", "(J)I",    (void*) getByteBlockHash },
+    {"nativeGetNonce",         "(JI)J",   (void*) getNonce },
+    {"nativeSetNonce",         "(JIJ)Z",  (void*) setNonce },
+};
+
+static const char* kClassName = "android/app/PropertyInvalidatedCache";
+
+} // anonymous namespace
+
+namespace android {
+
+int register_android_app_PropertyInvalidatedCache(JNIEnv* env) {
+    RegisterMethodsOrDie(env, kClassName, gMethods, NELEM(gMethods));
+    return JNI_OK;
+}
+
+} // namespace android
diff --git a/core/jni/android_app_PropertyInvalidatedCache.h b/core/jni/android_app_PropertyInvalidatedCache.h
new file mode 100644
index 0000000..eefa8fa
--- /dev/null
+++ b/core/jni/android_app_PropertyInvalidatedCache.h
@@ -0,0 +1,146 @@
+/*
+ * 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 <string.h>
+#include <memory.h>
+
+#include <atomic>
+
+namespace android {
+namespace app {
+namespace PropertyInvalidatedCache {
+
+/**
+ * A cache nonce block contains an array of std::atomic<int64_t> and an array of bytes.  The
+ * byte array has an associated hash.  This class provides methods to read and write the fields
+ * of the block but it does not interpret the fields.
+ *
+ * On initialization, all fields are set to zero.
+ *
+ * In general, methods do not report errors.  This allows the methods to be used in
+ * CriticalNative JNI APIs.
+ *
+ * The template is parameterized by the number of nonces it supports and the number of bytes in
+ * the string block.
+ */
+template<int maxNonce, size_t maxByte> class CacheNonce {
+
+    // The value of an unset field.
+    static const int UNSET = 0;
+
+    // A convenient typedef.  The jbyteArray element type is jbyte, which the compiler treats as
+    // signed char.
+    typedef signed char block_t;
+
+    // The array of nonces
+    volatile std::atomic<int64_t> mNonce[maxNonce];
+
+    // The byte array.  This is not atomic but it is guarded by the mByteHash.
+    volatile block_t mByteBlock[maxByte];
+
+    // The hash that validates the byte block
+    volatile std::atomic<int32_t> mByteHash;
+
+    // Pad the class to a multiple of 8 bytes.
+    int32_t _pad;
+
+  public:
+
+    // The expected size of this instance.  This is a compile-time constant and can be used in a
+    // static assertion.
+    static const int expectedSize =
+            maxNonce * sizeof(std::atomic<int64_t>)
+            + sizeof(std::atomic<int32_t>)
+            + maxByte * sizeof(block_t)
+            + sizeof(int32_t);
+
+    // These provide run-time access to the sizing parameters.
+    int getMaxNonce() const {
+        return maxNonce;
+    }
+
+    size_t getMaxByte() const {
+        return maxByte;
+    }
+
+    // Construct and initialize the memory.
+    CacheNonce() {
+        for (int i = 0; i < maxNonce; i++) {
+            mNonce[i] = UNSET;
+        }
+        mByteHash = UNSET;
+        memset((void*) mByteBlock, UNSET, sizeof(mByteBlock));
+    }
+
+    // Fetch a nonce, returning UNSET if the index is out of range.  This method specifically
+    // does not throw or generate an error if the index is out of range; this allows the method
+    // to be called in a CriticalNative JNI API.
+    int64_t getNonce(int index) const {
+        if (index < 0 || index >= maxNonce) {
+            return UNSET;
+        } else {
+            return mNonce[index];
+        }
+    }
+
+    // Set a nonce and return true. Return false if the index is out of range.  This method
+    // specifically does not throw or generate an error if the index is out of range; this
+    // allows the method to be called in a CriticalNative JNI API.
+    bool setNonce(int index, int64_t value) {
+        if (index < 0 || index >= maxNonce) {
+            return false;
+        } else {
+            mNonce[index] = value;
+            return true;
+        }
+    }
+
+    // Fetch just the byte-block hash
+    int32_t getHash() const {
+        return mByteHash;
+    }
+
+    // Copy the byte block to the target and return the current hash.
+    int32_t getByteBlock(block_t* block, size_t len) const {
+        memcpy(block, (void*) mByteBlock, std::min(maxByte, len));
+        return mByteHash;
+    }
+
+    // Set the byte block and the hash.
+    void setByteBlock(int hash, const block_t* block, size_t len) {
+        memcpy((void*) mByteBlock, block, len = std::min(maxByte, len));
+        mByteHash = hash;
+    }
+};
+
+/**
+ * Sizing parameters for the system_server PropertyInvalidatedCache support.  A client can
+ * retrieve the values through the accessors in CacheNonce instances.
+ */
+static const int MAX_NONCE = 64;
+static const int BYTE_BLOCK_SIZE = 8192;
+
+// The CacheNonce for system server holds 64 nonces with a string block of 8192 bytes.
+typedef CacheNonce<MAX_NONCE, BYTE_BLOCK_SIZE> SystemCacheNonce;
+
+// The goal of this assertion is to ensure that the data structure is the same size across 32-bit
+// and 64-bit systems.
+static_assert(sizeof(SystemCacheNonce) == SystemCacheNonce::expectedSize,
+              "Unexpected SystemCacheNonce size");
+
+} // namespace PropertyInvalidatedCache
+} // namespace app
+} // namespace android
diff --git a/core/jni/android_content_res_ApkAssets.cpp b/core/jni/android_content_res_ApkAssets.cpp
index 0b801b9..ded1a99 100644
--- a/core/jni/android_content_res_ApkAssets.cpp
+++ b/core/jni/android_content_res_ApkAssets.cpp
@@ -16,22 +16,21 @@
 
 #define ATRACE_TAG ATRACE_TAG_RESOURCES
 
-#include <mutex>
+#include "android_content_res_ApkAssets.h"
 
-#include "signal.h"
+#include <mutex>
 
 #include "android-base/logging.h"
 #include "android-base/macros.h"
 #include "android-base/stringprintf.h"
 #include "android-base/unique_fd.h"
 #include "androidfw/ApkAssets.h"
-#include "utils/misc.h"
-#include "utils/Trace.h"
-
-#include "android_content_res_ApkAssets.h"
 #include "core_jni_helpers.h"
 #include "jni.h"
 #include "nativehelper/ScopedUtfChars.h"
+#include "signal.h"
+#include "utils/Trace.h"
+#include "utils/misc.h"
 
 using ::android::base::unique_fd;
 
@@ -266,6 +265,20 @@
   return CreateGuardedApkAssets(std::move(apk_assets));
 }
 
+#if defined(_WIN32)
+int DupFdCloExec(int fd) {
+    int newfd = dup(fd);
+    fprintf(stderr, "duping %d to %d", fd, newfd);
+    return newfd;
+}
+#else
+int DupFdCloExec(int fd) {
+    int newfd = fcntl(fd, F_DUPFD_CLOEXEC, 0);
+    fprintf(stderr, "duping %d to %d", fd, newfd);
+    return newfd;
+}
+#endif
+
 static jlong NativeLoadFromFd(JNIEnv* env, jclass /*clazz*/, const format_type_t format,
                               jobject file_descriptor, jstring friendly_name,
                               const jint property_flags, jobject assets_provider) {
@@ -282,7 +295,7 @@
     return 0;
   }
 
-  unique_fd dup_fd(::fcntl(fd, F_DUPFD_CLOEXEC, 0));
+  unique_fd dup_fd(DupFdCloExec(fd));
   if (dup_fd < 0) {
     jniThrowIOException(env, errno);
     return 0;
@@ -349,7 +362,7 @@
     return 0;
   }
 
-  unique_fd dup_fd(::fcntl(fd, F_DUPFD_CLOEXEC, 0));
+  unique_fd dup_fd(DupFdCloExec(fd));
   if (dup_fd < 0) {
     jniThrowIOException(env, errno);
     return 0;
diff --git a/core/jni/android_hardware_Camera.cpp b/core/jni/android_hardware_Camera.cpp
index 10e49ef..50252c1 100644
--- a/core/jni/android_hardware_Camera.cpp
+++ b/core/jni/android_hardware_Camera.cpp
@@ -27,6 +27,7 @@
 #include <camera/StringUtils.h>
 #include <com_android_internal_camera_flags.h>
 #include <cutils/properties.h>
+#include <gui/Flags.h>
 #include <gui/GLConsumer.h>
 #include <gui/Surface.h>
 #include <nativehelper/JNIHelp.h>
@@ -715,16 +716,20 @@
     sp<Camera> camera = get_native_camera(env, thiz, NULL);
     if (camera == 0) return;
 
-    sp<IGraphicBufferProducer> gbp;
     sp<Surface> surface;
     if (jSurface) {
         surface = android_view_Surface_getSurface(env, jSurface);
-        if (surface != NULL) {
-            gbp = surface->getIGraphicBufferProducer();
-        }
     }
 
+#if WB_LIBCAMERASERVICE_WITH_DEPENDENCIES
+    if (camera->setPreviewTarget(surface) != NO_ERROR) {
+#else
+    sp<IGraphicBufferProducer> gbp;
+    if (surface != NULL) {
+        gbp = surface->getIGraphicBufferProducer();
+    }
     if (camera->setPreviewTarget(gbp) != NO_ERROR) {
+#endif
         jniThrowException(env, "java/io/IOException", "setPreviewTexture failed");
     }
 }
@@ -736,6 +741,9 @@
     sp<Camera> camera = get_native_camera(env, thiz, NULL);
     if (camera == 0) return;
 
+#if WB_LIBCAMERASERVICE_WITH_DEPENDENCIES
+    sp<Surface> surface;
+#endif
     sp<IGraphicBufferProducer> producer = NULL;
     if (jSurfaceTexture != NULL) {
         producer = SurfaceTexture_getProducer(env, jSurfaceTexture);
@@ -744,10 +752,16 @@
                     "SurfaceTexture already released in setPreviewTexture");
             return;
         }
-
+#if WB_LIBCAMERASERVICE_WITH_DEPENDENCIES
+        surface = new Surface(producer);
+#endif
     }
 
+#if WB_LIBCAMERASERVICE_WITH_DEPENDENCIES
+    if (camera->setPreviewTarget(surface) != NO_ERROR) {
+#else
     if (camera->setPreviewTarget(producer) != NO_ERROR) {
+#endif
         jniThrowException(env, "java/io/IOException",
                 "setPreviewTexture failed");
     }
@@ -761,18 +775,32 @@
     sp<Camera> camera = get_native_camera(env, thiz, &context);
     if (camera == 0) return;
 
+#if !WB_LIBCAMERASERVICE_WITH_DEPENDENCIES
     sp<IGraphicBufferProducer> gbp;
+#endif
     sp<Surface> surface;
     if (jSurface) {
         surface = android_view_Surface_getSurface(env, jSurface);
+        if (surface == NULL) {
+            jniThrowException(env, "java/lang/IllegalArgumentException",
+                              "android_view_Surface_getSurface failed");
+            return;
+        }
+
+#if !WB_LIBCAMERASERVICE_WITH_DEPENDENCIES
         if (surface != NULL) {
             gbp = surface->getIGraphicBufferProducer();
         }
+#endif
     }
     // Clear out normal preview callbacks
     context->setCallbackMode(env, false, false);
     // Then set up callback surface
+#if WB_LIBCAMERASERVICE_WITH_DEPENDENCIES
+    if (camera->setPreviewCallbackTarget(surface) != NO_ERROR) {
+#else
     if (camera->setPreviewCallbackTarget(gbp) != NO_ERROR) {
+#endif
         jniThrowException(env, "java/io/IOException", "setPreviewCallbackTarget failed");
     }
 }
diff --git a/core/jni/android_text_Hyphenator.cpp b/core/jni/android_text_Hyphenator.cpp
index 89fdeeb..933781c 100644
--- a/core/jni/android_text_Hyphenator.cpp
+++ b/core/jni/android_text_Hyphenator.cpp
@@ -14,17 +14,19 @@
  * limitations under the License.
  */
 
+#include <core_jni_helpers.h>
+#include <cutils/trace.h>
 #include <fcntl.h>
+#include <minikin/Hyphenator.h>
 #include <sys/mman.h>
 #include <sys/stat.h>
 #include <sys/types.h>
+#include <tracing_perfetto.h>
+#include <unicode/uloc.h>
 #include <unistd.h>
 
 #include <algorithm>
 
-#include <core_jni_helpers.h>
-#include <minikin/Hyphenator.h>
-
 namespace android {
 
 static std::string buildFileName(const std::string& locale) {
@@ -79,6 +81,23 @@
     minikin::addHyphenatorAlias(from, to);
 }
 
+/*
+ * Cache the subtag key map by calling uloc_forLanguageTag with a subtag.
+ * minikin calls uloc_forLanguageTag with an Unicode extension specifying
+ * the line breaking strictness. Parsing the extension requires loading the key map
+ * from keyTypeData.res in the ICU.
+ * "lb" is the key commonly used by minikin. "ca" is a common legacy key mapping to
+ * the "calendar" key. It ensures that the key map is loaded and cached in icu4c.
+ * "en-Latn-US" is a common locale used in the Android system regardless what default locale
+ * is selected in the Settings app.
+ */
+inline static void cacheUnicodeExtensionSubtagsKeyMap() {
+    UErrorCode status = U_ZERO_ERROR;
+    char localeID[ULOC_FULLNAME_CAPACITY] = {};
+    uloc_forLanguageTag("en-Latn-US-u-lb-loose-ca-gregory", localeID, ULOC_FULLNAME_CAPACITY,
+                        nullptr, &status);
+}
+
 static void init() {
     // TODO: Confirm that these are the best values. Various sources suggest (1, 1), but that
     // appears too small.
@@ -190,6 +209,10 @@
     addHyphenatorAlias("und-Orya", "or");  // Oriya
     addHyphenatorAlias("und-Taml", "ta");  // Tamil
     addHyphenatorAlias("und-Telu", "te");  // Telugu
+
+    tracing_perfetto::traceBegin(ATRACE_TAG_VIEW, "CacheUnicodeExtensionSubtagsKeyMap");
+    cacheUnicodeExtensionSubtagsKeyMap();
+    tracing_perfetto::traceEnd(ATRACE_TAG_VIEW); // CacheUnicodeExtensionSubtagsKeyMap
 }
 
 static const JNINativeMethod gMethods[] = {
diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp
index 7fe6731b..57bfc70 100644
--- a/core/jni/android_util_AssetManager.cpp
+++ b/core/jni/android_util_AssetManager.cpp
@@ -21,11 +21,9 @@
 
 #include <errno.h>
 #include <inttypes.h>
-#include <linux/capability.h>
 #include <stdio.h>
 #include <sys/stat.h>
 #include <sys/types.h>
-#include <sys/wait.h>
 #include <unistd.h>
 
 #include <sstream>
@@ -55,9 +53,6 @@
 #include "utils/Trace.h"
 #include "utils/misc.h"
 
-extern "C" int capget(cap_user_header_t hdrp, cap_user_data_t datap);
-extern "C" int capset(cap_user_header_t hdrp, const cap_user_data_t datap);
-
 using ::android::base::StringPrintf;
 
 namespace android {
@@ -105,11 +100,22 @@
 
 static struct parcel_file_descriptor_offsets_t {
   jclass mClass;
-  jmethodID mAdoptFd;
+  jmethodID mConstructor;
 } gParcelFileDescriptorOffsets;
 
+static struct file_descriptor_offsets_t {
+    jclass mClass;
+    jmethodID mConstructor;
+    jfieldID mHandle;
+} gFileDescriptorOffsets;
+
 static jclass g_stringClass = nullptr;
 
+// Duplicates a file descriptor. On Linux/Mac, this wraps fcntl(fd, F_DUPFD_CLOEXEC).
+// On windows, since file descriptors are not inherited by child processes by default, this
+// wraps dup()
+extern int DupFdCloExec(int fd);
+
 // ----------------------------------------------------------------------------
 
 // Java asset cookies have 0 as an invalid cookie, but TypedArray expects < 0.
@@ -272,9 +278,14 @@
 
   env->ReleasePrimitiveArrayCritical(out_offsets, offsets, 0);
 
-  return env->CallStaticObjectMethod(gParcelFileDescriptorOffsets.mClass,
-                                     gParcelFileDescriptorOffsets.mAdoptFd,
-                                     fd);
+  jobject fdescObj =
+          env->NewObject(gFileDescriptorOffsets.mClass, gFileDescriptorOffsets.mConstructor, fd);
+#ifdef _WIN32
+  env->SetLongField(fdescObj, gFileDescriptorOffsets.mHandle, _get_osfhandle(fd));
+#endif
+
+  return env->NewObject(gParcelFileDescriptorOffsets.mClass,
+                        gParcelFileDescriptorOffsets.mConstructor, fdescObj);
 }
 
 static jint NativeGetGlobalAssetCount(JNIEnv* /*env*/, jobject /*clazz*/) {
@@ -644,7 +655,7 @@
     return 0;
   }
 
-  base::unique_fd dup_fd(::fcntl(fd, F_DUPFD_CLOEXEC, 0));
+  base::unique_fd dup_fd(DupFdCloExec(fd));
   if (dup_fd < 0) {
     jniThrowIOException(env, errno);
     return 0;
@@ -1682,8 +1693,17 @@
 
   jclass pfdClass = FindClassOrDie(env, "android/os/ParcelFileDescriptor");
   gParcelFileDescriptorOffsets.mClass = MakeGlobalRefOrDie(env, pfdClass);
-  gParcelFileDescriptorOffsets.mAdoptFd =
-      GetStaticMethodIDOrDie(env, pfdClass, "adoptFd", "(I)Landroid/os/ParcelFileDescriptor;");
+  gParcelFileDescriptorOffsets.mConstructor =
+          GetMethodIDOrDie(env, pfdClass, "<init>", "(Ljava/io/FileDescriptor;)V");
+
+  jclass fdClass = FindClassOrDie(env, "java/io/FileDescriptor");
+  gFileDescriptorOffsets.mClass = MakeGlobalRefOrDie(env, fdClass);
+  gFileDescriptorOffsets.mConstructor =
+          GetMethodIDOrDie(env, gFileDescriptorOffsets.mClass, "<init>", "(I)V");
+#ifdef _WIN32
+  gFileDescriptorOffsets.mHandle =
+          GetFieldIDOrDie(env, gFileDescriptorOffsets.mClass, "handle", "J");
+#endif
 
   return RegisterMethodsOrDie(env, "android/content/res/AssetManager", gAssetManagerMethods,
                               NELEM(gAssetManagerMethods));
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 6d7aa5b..7fefe17 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -35,6 +35,7 @@
 #include <android_runtime/android_view_SurfaceSession.h>
 #include <cutils/ashmem.h>
 #include <gui/ISurfaceComposer.h>
+#include <gui/JankInfo.h>
 #include <gui/Surface.h>
 #include <gui/SurfaceComposerClient.h>
 #include <jni.h>
@@ -2176,9 +2177,30 @@
         jobjectArray jJankDataArray = env->NewObjectArray(jankData.size(),
                 gJankDataClassInfo.clazz, nullptr);
         for (size_t i = 0; i < jankData.size(); i++) {
+            // The exposed constants in SurfaceControl are simplified, so we need to translate the
+            // jank type we get from SF to what is exposed in Java.
+            int sfJankType = jankData[i].jankType;
+            int javaJankType = 0x0; // SurfaceControl.JankData.JANK_NONE
+            if (sfJankType &
+                (JankType::DisplayHAL | JankType::SurfaceFlingerCpuDeadlineMissed |
+                 JankType::SurfaceFlingerGpuDeadlineMissed | JankType::PredictionError |
+                 JankType::SurfaceFlingerScheduling)) {
+                javaJankType |= 0x1; // SurfaceControl.JankData.JANK_COMPOSER
+            }
+            if (sfJankType & JankType::AppDeadlineMissed) {
+                javaJankType |= 0x2; // SurfaceControl.JankData.JANK_APPLICATION
+            }
+            if (sfJankType &
+                ~(JankType::DisplayHAL | JankType::SurfaceFlingerCpuDeadlineMissed |
+                  JankType::SurfaceFlingerGpuDeadlineMissed | JankType::AppDeadlineMissed |
+                  JankType::PredictionError | JankType::SurfaceFlingerScheduling |
+                  JankType::BufferStuffing | JankType::SurfaceFlingerStuffing)) {
+                javaJankType |= 0x4; // SurfaceControl.JankData.JANK_OTHER
+            }
+
             jobject jJankData =
                     env->NewObject(gJankDataClassInfo.clazz, gJankDataClassInfo.ctor,
-                                   jankData[i].frameVsyncId, jankData[i].jankType,
+                                   jankData[i].frameVsyncId, javaJankType,
                                    jankData[i].frameIntervalNs, jankData[i].scheduledAppFrameTimeNs,
                                    jankData[i].actualAppFrameTimeNs);
             env->SetObjectArrayElement(jJankDataArray, i, jJankData);
diff --git a/core/jni/com_android_internal_os_ApplicationSharedMemory.cpp b/core/jni/com_android_internal_os_ApplicationSharedMemory.cpp
index 453e539..cc1687c 100644
--- a/core/jni/com_android_internal_os_ApplicationSharedMemory.cpp
+++ b/core/jni/com_android_internal_os_ApplicationSharedMemory.cpp
@@ -29,8 +29,12 @@
 
 #include "core_jni_helpers.h"
 
+#include "android_app_PropertyInvalidatedCache.h"
+
 namespace {
 
+using namespace android::app::PropertyInvalidatedCache;
+
 // Atomics should be safe to use across processes if they are lock free.
 static_assert(std::atomic<int64_t>::is_always_lock_free == true,
               "atomic<int64_t> is not always lock free");
@@ -64,12 +68,15 @@
     void setLatestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis(int64_t offset) {
         latestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis = offset;
     }
+
+    // The nonce storage for pic.  The sizing is suitable for the system server module.
+    SystemCacheNonce systemPic;
 };
 
 // Update the expected value when modifying the members of SharedMemory.
 // The goal of this assertion is to ensure that the data structure is the same size across 32-bit
 // and 64-bit systems.
-static_assert(sizeof(SharedMemory) == 8, "Unexpected SharedMemory size");
+static_assert(sizeof(SharedMemory) == 8 + sizeof(SystemCacheNonce), "Unexpected SharedMemory size");
 
 static jint nativeCreate(JNIEnv* env, jclass) {
     // Create anonymous shared memory region
@@ -133,6 +140,12 @@
     return sharedMemory->getLatestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis();
 }
 
+// This is a FastNative method.  It takes the usual JNIEnv* and jclass* arguments.
+static jlong nativeGetSystemNonceBlock(JNIEnv*, jclass*, jlong ptr) {
+    SharedMemory* sharedMemory = reinterpret_cast<SharedMemory*>(ptr);
+    return reinterpret_cast<jlong>(&sharedMemory->systemPic);
+}
+
 static const JNINativeMethod gMethods[] = {
         {"nativeCreate", "()I", (void*)nativeCreate},
         {"nativeMap", "(IZ)J", (void*)nativeMap},
@@ -143,16 +156,17 @@
          (void*)nativeSetLatestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis},
         {"nativeGetLatestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis", "(J)J",
          (void*)nativeGetLatestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis},
+        {"nativeGetSystemNonceBlock", "(J)J", (void*) nativeGetSystemNonceBlock},
 };
 
-} // anonymous namespace
-
-namespace android {
-
 static const char kApplicationSharedMemoryClassName[] =
         "com/android/internal/os/ApplicationSharedMemory";
 static jclass gApplicationSharedMemoryClass;
 
+} // anonymous namespace
+
+namespace android {
+
 int register_com_android_internal_os_ApplicationSharedMemory(JNIEnv* env) {
     gApplicationSharedMemoryClass =
             MakeGlobalRefOrDie(env, FindClassOrDie(env, kApplicationSharedMemoryClassName));
diff --git a/core/jni/platform/host/HostRuntime.cpp b/core/jni/platform/host/HostRuntime.cpp
index 24551a4..3747299 100644
--- a/core/jni/platform/host/HostRuntime.cpp
+++ b/core/jni/platform/host/HostRuntime.cpp
@@ -114,10 +114,8 @@
 static const std::unordered_map<std::string, RegJNIRec> gRegJNIMap = {
         {"android.animation.PropertyValuesHolder",
          REG_JNI(register_android_animation_PropertyValuesHolder)},
-#ifdef __linux__
         {"android.content.res.ApkAssets", REG_JNI(register_android_content_res_ApkAssets)},
         {"android.content.res.AssetManager", REG_JNI(register_android_content_AssetManager)},
-#endif
         {"android.content.res.StringBlock", REG_JNI(register_android_content_StringBlock)},
         {"android.content.res.XmlBlock", REG_JNI(register_android_content_XmlBlock)},
 #ifdef __linux__
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index fb06e96..561d351 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -851,6 +851,7 @@
     <protected-broadcast android:name="android.telephony.action.ACTION_SATELLITE_SUBSCRIBER_ID_LIST_CHANGED" />
     <protected-broadcast android:name="android.service.ondeviceintelligence.MODEL_LOADED" />
     <protected-broadcast android:name="android.service.ondeviceintelligence.MODEL_UNLOADED" />
+    <protected-broadcast android:name="android.telephony.action.ACTION_SATELLITE_START_NON_EMERGENCY_SESSION" />
 
 
     <!-- ====================================================================== -->
@@ -8483,6 +8484,27 @@
     <permission android:name="android.permission.RESERVED_FOR_TESTING_SIGNATURE"
                 android:protectionLevel="signature"/>
 
+    <!--
+        @SystemApi
+        @FlaggedApi("android.media.tv.flags.media_quality_fw")
+        Allows an application to access its picture profile from the media quality database.
+        <p> Protection level: signature|privileged|vendor privileged
+        @hide
+    -->
+    <permission android:name="android.permission.MANAGE_GLOBAL_PICTURE_QUALITY_SERVICE"
+                android:protectionLevel="signature|privileged|vendorPrivileged"
+                android:featureFlag="android.media.tv.flags.media_quality_fw"/>
+
+    <!--
+        @SystemApi
+        @FlaggedApi("android.media.tv.flags.media_quality_fw")
+        Allows an application to access its sound profile from the media quality database.
+        <p> Protection level: signature|privileged|vendor privileged
+        @hide
+    -->
+    <permission android:name="android.permission.MANAGE_GLOBAL_SOUND_QUALITY_SERVICE"
+                android:protectionLevel="signature|privileged|vendorPrivileged"
+                android:featureFlag="android.media.tv.flags.media_quality_fw"/>
     <!-- @SystemApi
         @FlaggedApi("android.content.pm.verification_service")
         Allows app to be the verification agent to verify packages.
diff --git a/core/res/res/color-watch-v36/btn_material_filled_background_color.xml b/core/res/res/color-watch-v36/btn_material_filled_background_color.xml
index 8b2afa8..70aace4 100644
--- a/core/res/res/color-watch-v36/btn_material_filled_background_color.xml
+++ b/core/res/res/color-watch-v36/btn_material_filled_background_color.xml
@@ -18,6 +18,5 @@
     <item android:state_enabled="false"
           android:alpha="?attr/disabledAlpha"
           android:color="?attr/materialColorOnSurface" />
-    <item android:state_enabled="true"
-          android:color="?attr/materialColorPrimary" />
+    <item android:color="?attr/materialColorPrimary" />
 </selector>
\ No newline at end of file
diff --git a/core/res/res/color-watch-v36/btn_material_filled_tonal_text_color.xml b/core/res/res/color-watch-v36/btn_material_filled_content_color.xml
similarity index 89%
rename from core/res/res/color-watch-v36/btn_material_filled_tonal_text_color.xml
rename to core/res/res/color-watch-v36/btn_material_filled_content_color.xml
index 94e50fb..4cc8fe5 100644
--- a/core/res/res/color-watch-v36/btn_material_filled_tonal_text_color.xml
+++ b/core/res/res/color-watch-v36/btn_material_filled_content_color.xml
@@ -18,6 +18,5 @@
     <item android:state_enabled="false"
           android:alpha="?attr/primaryContentAlpha"
           android:color="?attr/materialColorOnSurface" />
-    <item android:state_enabled="true"
-          android:color="?attr/materialColorOnSurface" />
+    <item android:color="?attr/materialColorOnPrimary" />
 </selector>
\ No newline at end of file
diff --git a/core/res/res/color-watch-v36/btn_material_filled_text_color.xml b/core/res/res/color-watch-v36/btn_material_filled_text_color.xml
deleted file mode 100644
index cefc912..0000000
--- a/core/res/res/color-watch-v36/btn_material_filled_text_color.xml
+++ /dev/null
@@ -1,23 +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.
-  -->
-
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:state_enabled="false"
-          android:alpha="?attr/primaryContentAlpha"
-          android:color="?attr/materialColorOnSurface" />
-    <item android:state_enabled="true"
-          android:color="?attr/materialColorOnPrimary" />
-</selector>
\ No newline at end of file
diff --git a/core/res/res/color-watch-v36/btn_material_filled_tonal_background_color.xml b/core/res/res/color-watch-v36/btn_material_filled_tonal_background_color.xml
index eaf9e7d..b2a25af 100644
--- a/core/res/res/color-watch-v36/btn_material_filled_tonal_background_color.xml
+++ b/core/res/res/color-watch-v36/btn_material_filled_tonal_background_color.xml
@@ -18,6 +18,5 @@
     <item android:state_enabled="false"
           android:alpha="?attr/disabledAlpha"
           android:color="?attr/materialColorOnSurface" />
-    <item android:state_enabled="true"
-          android:color="?attr/materialColorSurfaceContainer" />
+    <item android:color="?attr/materialColorSurfaceContainer" />
 </selector>
\ No newline at end of file
diff --git a/core/res/res/color-watch-v36/btn_material_filled_tonal_text_color.xml b/core/res/res/color-watch-v36/btn_material_filled_tonal_content_color.xml
similarity index 89%
copy from core/res/res/color-watch-v36/btn_material_filled_tonal_text_color.xml
copy to core/res/res/color-watch-v36/btn_material_filled_tonal_content_color.xml
index 94e50fb..59810356 100644
--- a/core/res/res/color-watch-v36/btn_material_filled_tonal_text_color.xml
+++ b/core/res/res/color-watch-v36/btn_material_filled_tonal_content_color.xml
@@ -18,6 +18,5 @@
     <item android:state_enabled="false"
           android:alpha="?attr/primaryContentAlpha"
           android:color="?attr/materialColorOnSurface" />
-    <item android:state_enabled="true"
-          android:color="?attr/materialColorOnSurface" />
+    <item android:color="?attr/materialColorOnSurface" />
 </selector>
\ No newline at end of file
diff --git a/core/res/res/drawable-watch-v36/dialog_alert_button_background_negative.xml b/core/res/res/drawable-watch-v36/dialog_alert_button_background_negative.xml
new file mode 100644
index 0000000..b6b8eac3
--- /dev/null
+++ b/core/res/res/drawable-watch-v36/dialog_alert_button_background_negative.xml
@@ -0,0 +1,25 @@
+<?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.
+  -->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+       android:shape="rectangle">
+    <solid android:color="@color/btn_material_filled_tonal_background_color"/>
+    <corners android:radius="@dimen/config_bottomDialogCornerRadius" />
+    <size
+        android:width="@dimen/dialog_btn_negative_width"
+        android:height="@dimen/dialog_btn_negative_height" />
+</shape>
diff --git a/core/res/res/drawable-watch-v36/dialog_alert_button_background_positive.xml b/core/res/res/drawable-watch-v36/dialog_alert_button_background_positive.xml
new file mode 100644
index 0000000..92262fb
--- /dev/null
+++ b/core/res/res/drawable-watch-v36/dialog_alert_button_background_positive.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.
+  -->
+
+<rotate xmlns:android="http://schemas.android.com/apk/res/android"
+        android:fromDegrees="-45"
+        android:toDegrees="-45"
+        android:pivotX="50%"
+        android:pivotY="50%">
+    <shape android:shape="rectangle">
+        <solid android:color="@color/btn_material_filled_background_color"/>
+        <corners android:radius="200dp" />
+        <size
+            android:width="63dp"
+            android:height="54dp" />
+    </shape>
+</rotate>
\ No newline at end of file
diff --git a/core/res/res/color-watch-v36/btn_material_filled_tonal_text_color.xml b/core/res/res/drawable-watch-v36/dialog_alert_button_negative.xml
similarity index 65%
copy from core/res/res/color-watch-v36/btn_material_filled_tonal_text_color.xml
copy to core/res/res/drawable-watch-v36/dialog_alert_button_negative.xml
index 94e50fb..c155ba1 100644
--- a/core/res/res/color-watch-v36/btn_material_filled_tonal_text_color.xml
+++ b/core/res/res/drawable-watch-v36/dialog_alert_button_negative.xml
@@ -14,10 +14,9 @@
   ~ limitations under the License.
   -->
 
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:state_enabled="false"
-          android:alpha="?attr/primaryContentAlpha"
-          android:color="?attr/materialColorOnSurface" />
-    <item android:state_enabled="true"
-          android:color="?attr/materialColorOnSurface" />
-</selector>
\ No newline at end of file
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:drawable="@drawable/dialog_alert_button_background_negative"/>
+    <item
+        android:drawable="@drawable/ic_close"
+        android:gravity="center" />
+</layer-list>
\ No newline at end of file
diff --git a/core/res/res/color-watch-v36/btn_material_filled_tonal_text_color.xml b/core/res/res/drawable-watch-v36/dialog_alert_button_positive.xml
similarity index 65%
copy from core/res/res/color-watch-v36/btn_material_filled_tonal_text_color.xml
copy to core/res/res/drawable-watch-v36/dialog_alert_button_positive.xml
index 94e50fb..01ab073 100644
--- a/core/res/res/color-watch-v36/btn_material_filled_tonal_text_color.xml
+++ b/core/res/res/drawable-watch-v36/dialog_alert_button_positive.xml
@@ -14,10 +14,9 @@
   ~ limitations under the License.
   -->
 
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:state_enabled="false"
-          android:alpha="?attr/primaryContentAlpha"
-          android:color="?attr/materialColorOnSurface" />
-    <item android:state_enabled="true"
-          android:color="?attr/materialColorOnSurface" />
-</selector>
\ No newline at end of file
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:drawable="@drawable/dialog_alert_button_background_positive"/>
+    <item
+        android:drawable="@drawable/ic_check"
+        android:gravity="center" />
+</layer-list>
\ No newline at end of file
diff --git a/core/res/res/drawable-watch-v36/ic_check.xml b/core/res/res/drawable-watch-v36/ic_check.xml
new file mode 100644
index 0000000..7b01e64
--- /dev/null
+++ b/core/res/res/drawable-watch-v36/ic_check.xml
@@ -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.
+  -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="28dp"
+        android:height="28dp"
+        android:viewportWidth="960"
+        android:viewportHeight="960"
+        android:tint="@color/btn_material_filled_content_color">
+    <path android:fillColor="@color/btn_material_filled_content_color"
+          android:pathData="M382,597.87L716.7,263.17Q730.37,249.5 748.76,249.5Q767.15,249.5 780.83,263.17Q794.5,276.85 794.5,295.62Q794.5,314.39 780.83,328.07L414.07,695.59Q400.39,709.26 382,709.26Q363.61,709.26 349.93,695.59L178.41,524.07Q164.74,510.39 165.12,491.62Q165.5,472.85 179.17,459.17Q192.85,445.5 211.62,445.5Q230.39,445.5 244.07,459.17L382,597.87Z"/>
+</vector>
diff --git a/core/res/res/drawable-watch-v36/ic_close.xml b/core/res/res/drawable-watch-v36/ic_close.xml
new file mode 100644
index 0000000..1f3da36
--- /dev/null
+++ b/core/res/res/drawable-watch-v36/ic_close.xml
@@ -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.
+  -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="28dp"
+        android:height="28dp"
+        android:viewportWidth="960"
+        android:viewportHeight="960"
+        android:tint="@color/btn_material_filled_tonal_content_color">
+    <path android:fillColor="@color/btn_material_filled_tonal_content_color"
+          android:pathData="M480,543.65L287.83,735.83Q275.15,748.5 256,748.5Q236.85,748.5 224.17,735.83Q211.5,723.15 211.5,704Q211.5,684.85 224.17,672.17L416.35,480L224.17,287.83Q211.5,275.15 211.5,256Q211.5,236.85 224.17,224.17Q236.85,211.5 256,211.5Q275.15,211.5 287.83,224.17L480,416.35L672.17,224.17Q684.85,211.5 704,211.5Q723.15,211.5 735.83,224.17Q748.5,236.85 748.5,256Q748.5,275.15 735.83,287.83L543.65,480L735.83,672.17Q748.5,684.85 748.5,704Q748.5,723.15 735.83,735.83Q723.15,748.5 704,748.5Q684.85,748.5 672.17,735.83L480,543.65Z"/>
+</vector>
diff --git a/core/res/res/layout-watch-v36/alert_dialog_material.xml b/core/res/res/layout-watch-v36/alert_dialog_material.xml
new file mode 100644
index 0000000..900102f
--- /dev/null
+++ b/core/res/res/layout-watch-v36/alert_dialog_material.xml
@@ -0,0 +1,123 @@
+<!--
+  ~ 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.
+  -->
+
+<!-- This layout is the AlertDialog template. It overrides the system layout with the same name.
+    Make sure to include all the existing id of the overridden alert_dialog_material.-->
+<com.android.internal.widget.WatchListDecorLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/parentPanel"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+    <ScrollView
+        android:id="@+id/scrollView"
+        android:fillViewport="true"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent">
+        <LinearLayout
+            android:orientation="vertical"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content">
+            <!-- Top Panel -->
+            <FrameLayout
+                android:paddingLeft="?dialogPreferredPadding"
+                android:paddingRight="?dialogPreferredPadding"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:id="@+id/topPanel"
+                android:minHeight="@dimen/dialog_list_padding_top_no_title">
+                <include android:id="@+id/title_template"
+                         android:layout_width="match_parent"
+                         android:layout_height="wrap_content"
+                         layout="@layout/alert_dialog_title_material"/>
+            </FrameLayout>
+
+            <!-- Content Panel -->
+            <FrameLayout android:id="@+id/contentPanel"
+                         android:layout_width="match_parent"
+                         android:layout_height="wrap_content"
+                         android:clipToPadding="false">
+                <TextView android:id="@+id/message"
+                          android:layout_width="match_parent"
+                          android:layout_height="wrap_content"
+                          android:gravity="center_horizontal|top"
+                          android:textAppearance="@style/TextAppearance.DeviceDefault.Body1"
+                          android:paddingStart="?dialogPreferredPadding"
+                          android:paddingEnd="?dialogPreferredPadding"
+                          android:paddingTop="8dip"
+                          android:paddingBottom="8dip"/>
+            </FrameLayout>
+
+            <!-- Custom Panel, to replace content panel if needed -->
+            <FrameLayout android:id="@+id/customPanel"
+                         android:layout_width="match_parent"
+                         android:layout_height="match_parent"
+                         android:minHeight="64dp">
+                <FrameLayout android:id="@+android:id/custom"
+                             android:layout_width="match_parent"
+                             android:layout_height="wrap_content" />
+            </FrameLayout>
+
+            <!-- Button Panel -->
+            <FrameLayout
+                android:id="@+id/buttonPanel"
+                android:minHeight="@dimen/dialog_list_padding_bottom_no_buttons"
+                android:layout_weight="1"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_gravity="center">
+                <LinearLayout
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:layout_gravity="bottom"
+                    android:orientation="horizontal"
+                    android:paddingBottom="?dialogPreferredPadding"
+                    style="?android:attr/buttonBarStyle"
+                    android:measureWithLargestChild="true">
+                    <Button android:id="@+id/button2"
+                            android:layout_width="wrap_content"
+                            android:layout_height="wrap_content"
+                            android:layout_gravity="center"
+                            android:gravity="center"
+                            android:layout_weight="1"
+                            style="@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog.Negative" />
+                    <Button android:id="@+id/button3"
+                            android:layout_width="wrap_content"
+                            android:layout_height="wrap_content"
+                            android:layout_gravity="center"
+                            android:gravity="center"
+                            android:layout_weight="1"
+                            style="?android:attr/buttonBarButtonStyle"/>
+                    <FrameLayout
+                        android:layout_width="wrap_content"
+                        android:layout_height="wrap_content">
+                        <Button android:id="@+id/button1"
+                                android:layout_width="match_parent"
+                                android:layout_height="match_parent"
+                                android:layout_gravity="center"
+                                android:gravity="center"
+                                android:layout_weight="1"
+                                style="@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog.Confirm"/>
+                        <!-- This works as background. -->
+                        <ImageView
+                            android:layout_width="match_parent"
+                            android:layout_height="match_parent"
+                            android:src="@drawable/dialog_alert_button_positive"/>
+                    </FrameLayout>
+                </LinearLayout>
+            </FrameLayout>
+        </LinearLayout>
+    </ScrollView>
+</com.android.internal.widget.WatchListDecorLayout>
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index cdae265..ccc584a 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -1943,6 +1943,8 @@
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Af"</string>
     <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
     <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g>-<xliff:g id="END">%2$s</xliff:g>"</string>
+    <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
+    <skip />
     <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Enige kalender"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> demp sekere klanke"</string>
     <string name="system_error_wipe_data" msgid="5910572292172208493">"Daar is \'n interne probleem met jou toestel en dit sal dalk onstabiel wees totdat jy \'n fabriekterugstelling doen."</string>
@@ -2424,6 +2426,14 @@
     <string name="satellite_notification_manual_summary" msgid="901206289846283445">"Stuur en ontvang boodskappe sonder ’n selfoon- of wi-fi-netwerk"</string>
     <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>
+    <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
+    <skip />
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Hangend …"</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Stel Vingerafdrukslot weer op"</string>
     <string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"<xliff:g id="FINGERPRINT">%s</xliff:g> kan nie meer herken word nie."</string>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index cc8ab3e..22dc49c 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -1943,6 +1943,8 @@
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"ጠፍቷል"</string>
     <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">"፣ "</string>
     <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> - <xliff:g id="END">%2$s</xliff:g>"</string>
+    <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
+    <skip />
     <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"ማንኛውም ቀን መቁጠሪያ"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> አንዳንድ ድምጾችን እየዘጋ ነው"</string>
     <string name="system_error_wipe_data" msgid="5910572292172208493">"መሣሪያዎ ላይ የውስጣዊ ችግር አለ፣ የፋብሪካ ውሂብ ዳግም እስኪያስጀምሩት ድረስ ላይረጋጋ ይችላል።"</string>
@@ -2424,6 +2426,14 @@
     <string name="satellite_notification_manual_summary" msgid="901206289846283445">"ያለ ሞባይል ወይም የWi-Fi አውታረ መረብ መልዕክቶችን ይላኩ እና ይቀበሉ"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"መልዕክቶች ይክፈቱ"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"እንዴት እንደሚሠራ"</string>
+    <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
+    <skip />
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"በመጠባበቅ ላይ..."</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"በጣት አሻራ መክፈቻን እንደገና ያዋቅሩ"</string>
     <string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"<xliff:g id="FINGERPRINT">%s</xliff:g> ከእንግዲህ መለየት አይችልም።"</string>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index e81ec89..1aaad07 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -1947,6 +1947,8 @@
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"غير مفعَّل"</string>
     <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">"، "</string>
     <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"من <xliff:g id="START">%1$s</xliff:g> إلى <xliff:g id="END">%2$s</xliff:g>"</string>
+    <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
+    <skip />
     <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"أي تقويم"</string>
     <string name="muted_by" msgid="91464083490094950">"يعمل <xliff:g id="THIRD_PARTY">%1$s</xliff:g> على كتم بعض الأصوات."</string>
     <string name="system_error_wipe_data" msgid="5910572292172208493">"حدثت مشكلة داخلية في جهازك، وقد لا يستقر وضعه حتى إجراء إعادة الضبط على الإعدادات الأصلية."</string>
@@ -2428,6 +2430,14 @@
     <string name="satellite_notification_manual_summary" msgid="901206289846283445">"‏يمكنك إرسال الرسائل واستلامها بدون شبكة الجوّال أو شبكة Wi-Fi"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"فتح تطبيق \"الرسائل\""</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"طريقة العمل"</string>
+    <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
+    <skip />
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"بانتظار الإزالة من الأرشيف…"</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"إعادة إعداد ميزة \"فتح الجهاز ببصمة الإصبع\""</string>
     <string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"لا يمكن بعد الآن التعرّف على \"<xliff:g id="FINGERPRINT">%s</xliff:g>\"."</string>
diff --git a/core/res/res/values-as/strings.xml b/core/res/res/values-as/strings.xml
index f36a659..74d8ed6 100644
--- a/core/res/res/values-as/strings.xml
+++ b/core/res/res/values-as/strings.xml
@@ -1943,6 +1943,8 @@
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"অফ আছে"</string>
     <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
     <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> - <xliff:g id="END">%2$s</xliff:g>"</string>
+    <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
+    <skip />
     <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"যিকোনো কেলেণ্ডাৰ"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g>এ কিছুমান ধ্বনি মিউট কৰি আছে"</string>
     <string name="system_error_wipe_data" msgid="5910572292172208493">"আপোনাৰ ডিভাইচত এটা আভ্যন্তৰীণ সমস্যা আছে আৰু আপুনি ফেক্টৰী ডেটা ৰিছেট নকৰালৈকে ই সুস্থিৰভাৱে কাম নকৰিব পাৰে।"</string>
@@ -2424,6 +2426,14 @@
     <string name="satellite_notification_manual_summary" msgid="901206289846283445">"আপুনি কোনো ম’বাইল বা ৱাই-ফাই নেটৱৰ্ক নোহোৱাকৈ বাৰ্তা পঠিয়াওক আৰু লাভ কৰক"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Messages খোলক"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"ই কেনেকৈ কাম কৰে"</string>
+    <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
+    <skip />
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"বিবেচনাধীন হৈ আছে..."</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"ফিংগাৰপ্ৰিণ্ট আনলক পুনৰ ছেট আপ কৰক"</string>
     <string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"<xliff:g id="FINGERPRINT">%s</xliff:g> আৰু চিনাক্ত কৰিব নোৱাৰি।"</string>
diff --git a/core/res/res/values-az/strings.xml b/core/res/res/values-az/strings.xml
index 2a77df2..22e86df 100644
--- a/core/res/res/values-az/strings.xml
+++ b/core/res/res/values-az/strings.xml
@@ -1943,6 +1943,8 @@
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Deaktiv"</string>
     <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
     <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> - <xliff:g id="END">%2$s</xliff:g>"</string>
+    <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
+    <skip />
     <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"İstənilən təqvim"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> bəzi səsləri səssiz rejimə salır"</string>
     <string name="system_error_wipe_data" msgid="5910572292172208493">"Cihazınızın daxili problemi var və istehsalçı sıfırlanması olmayana qədər qeyri-stabil ola bilər."</string>
@@ -2424,6 +2426,14 @@
     <string name="satellite_notification_manual_summary" msgid="901206289846283445">"Mobil və ya Wi-Fi şəbəkəsi olmadan mesajlar göndərin və qəbul edin"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Mesajı açın"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Haqqında"</string>
+    <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
+    <skip />
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Gözləmədə..."</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Barmaqla Kilidaçmanı yenidən ayarlayın"</string>
     <string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"<xliff:g id="FINGERPRINT">%s</xliff:g> artıq tanınmır."</string>
diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml
index a51aad2..b9f909f 100644
--- a/core/res/res/values-b+sr+Latn/strings.xml
+++ b/core/res/res/values-b+sr+Latn/strings.xml
@@ -1944,6 +1944,8 @@
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Isključeno"</string>
     <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
     <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g>–<xliff:g id="END">%2$s</xliff:g>"</string>
+    <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
+    <skip />
     <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Bilo koji kalendar"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> isključuje neke zvuke"</string>
     <string name="system_error_wipe_data" msgid="5910572292172208493">"Došlo je do internog problema u vezi sa uređajem i možda će biti nestabilan dok ne obavite resetovanje na fabrička podešavanja."</string>
@@ -2425,6 +2427,14 @@
     <string name="satellite_notification_manual_summary" msgid="901206289846283445">"Šaljite i primajte poruke bez mobilne ili WiFi mreže"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Otvori Messages"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Princip rada"</string>
+    <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
+    <skip />
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Na čekanju..."</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Ponovo podesite otključavanje otiskom prsta"</string>
     <string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"<xliff:g id="FINGERPRINT">%s</xliff:g> više ne može da se prepozna."</string>
diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml
index 26f11df..b252094 100644
--- a/core/res/res/values-be/strings.xml
+++ b/core/res/res/values-be/strings.xml
@@ -1945,6 +1945,8 @@
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Выключана"</string>
     <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
     <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g>-<xliff:g id="END">%2$s</xliff:g>"</string>
+    <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
+    <skip />
     <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Любы каляндар"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> выключае некаторыя гукі"</string>
     <string name="system_error_wipe_data" msgid="5910572292172208493">"На вашай прыладзе ўзнікла ўнутраная праблема, і яна можа працаваць нестабільна, пакуль вы не зробіце скід да заводскіх налад."</string>
@@ -2426,6 +2428,14 @@
     <string name="satellite_notification_manual_summary" msgid="901206289846283445">"Вы можаце адпраўляць і атрымліваць паведамленні, калі падключэнне да мабільнай сеткі або сеткі Wi-Fi адсутнічае"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Адкрыць Паведамленні"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Як гэта працуе"</string>
+    <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
+    <skip />
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"У чаканні..."</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Наладзіць разблакіроўку адбіткам пальца паўторна"</string>
     <string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"Адбітак пальца \"<xliff:g id="FINGERPRINT">%s</xliff:g>\" больш не можа быць распазнаны."</string>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index 38c9d0b..e5a08bb 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -1943,6 +1943,8 @@
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Изкл."</string>
     <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
     <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> – <xliff:g id="END">%2$s</xliff:g>"</string>
+    <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
+    <skip />
     <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Всички календари"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> заглушава някои звуци"</string>
     <string name="system_error_wipe_data" msgid="5910572292172208493">"Възникна вътрешен проблем с устройството ви. То може да е нестабилно, докато не възстановите фабричните настройки."</string>
@@ -2424,6 +2426,14 @@
     <string name="satellite_notification_manual_summary" msgid="901206289846283445">"Изпращайте и получавайте съобщения без мобилна или Wi-Fi мрежа"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Отваряне на Messages"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Начин на работа"</string>
+    <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
+    <skip />
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Изчаква..."</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Повторно настройване на „Отключване с отпечатък“"</string>
     <string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"<xliff:g id="FINGERPRINT">%s</xliff:g> вече не може да се разпознае."</string>
diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml
index 8a833c8..df8a908 100644
--- a/core/res/res/values-bn/strings.xml
+++ b/core/res/res/values-bn/strings.xml
@@ -1943,6 +1943,8 @@
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"বন্ধ আছে"</string>
     <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
     <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> - <xliff:g id="END">%2$s</xliff:g>"</string>
+    <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
+    <skip />
     <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"যেকোনও ক্যালেন্ডার"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> কিছু সাউন্ডকে মিউট করে দিচ্ছে"</string>
     <string name="system_error_wipe_data" msgid="5910572292172208493">"আপনার ডিভাইসে একটি অভ্যন্তরীন সমস্যা হয়েছে, এবং আপনি যতক্ষণ না পর্যন্ত এটিকে ফ্যাক্টরি ডেটা রিসেট করছেন ততক্ষণ এটি ঠিকভাবে কাজ নাও করতে পারে৷"</string>
@@ -2424,6 +2426,14 @@
     <string name="satellite_notification_manual_summary" msgid="901206289846283445">"কোনও মেবাইল বা ওয়াই-ফাই নেটওয়ার্ক ছাড়াই মেসেজ পাঠান ও রিসিভ করুন"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Messages খুলুন"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"এটি কীভাবে কাজ করে"</string>
+    <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
+    <skip />
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"বাকি আছে…"</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"\'ফিঙ্গারপ্রিন্ট আনলক\' আবার সেট-আপ করুন"</string>
     <string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"<xliff:g id="FINGERPRINT">%s</xliff:g> আর শনাক্ত করা যাবে না।"</string>
diff --git a/core/res/res/values-bs/strings.xml b/core/res/res/values-bs/strings.xml
index 9e29b7f..35e30ba 100644
--- a/core/res/res/values-bs/strings.xml
+++ b/core/res/res/values-bs/strings.xml
@@ -1944,6 +1944,8 @@
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Isključeno"</string>
     <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
     <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> – <xliff:g id="END">%2$s</xliff:g>"</string>
+    <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
+    <skip />
     <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Bilo koji kalendar"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> isključuje neke zvukove"</string>
     <string name="system_error_wipe_data" msgid="5910572292172208493">"Postoji problem u vašem uređaju i može biti nestabilan dok ga ne vratite na fabričke postavke."</string>
@@ -2425,6 +2427,14 @@
     <string name="satellite_notification_manual_summary" msgid="901206289846283445">"Šaljite i primajte poruke bez mobilne ili WiFi mreže"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Otvorite Messages"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Kako ovo funkcionira"</string>
+    <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
+    <skip />
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Na čekanju…"</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Ponovo postavite otključavanje otiskom prsta"</string>
     <string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"<xliff:g id="FINGERPRINT">%s</xliff:g> se više ne može prepoznati."</string>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index 0dd1bca..7c817b9 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -1944,6 +1944,8 @@
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Desactivat"</string>
     <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
     <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g>-<xliff:g id="END">%2$s</xliff:g>"</string>
+    <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
+    <skip />
     <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Qualsevol calendari"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> està silenciant alguns sons"</string>
     <string name="system_error_wipe_data" msgid="5910572292172208493">"S\'ha produït un error intern al dispositiu i és possible que funcioni de manera inestable fins que restableixis les dades de fàbrica."</string>
@@ -2425,6 +2427,14 @@
     <string name="satellite_notification_manual_summary" msgid="901206289846283445">"Envia i rep missatges sense una xarxa mòbil o Wi‑Fi"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Obre Missatges"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Com funciona"</string>
+    <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
+    <skip />
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Pendent..."</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Torna a configurar Desbloqueig amb empremta digital"</string>
     <string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"<xliff:g id="FINGERPRINT">%s</xliff:g> ja no es pot reconèixer."</string>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index 35f269a..a9766bf 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -1945,6 +1945,8 @@
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Vypnuto"</string>
     <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
     <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g>–<xliff:g id="END">%2$s</xliff:g>"</string>
+    <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
+    <skip />
     <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"V libovolném kalendáři"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> vypíná určité zvuky"</string>
     <string name="system_error_wipe_data" msgid="5910572292172208493">"V zařízení došlo k internímu problému. Dokud neprovedete obnovení továrních dat, může být nestabilní."</string>
@@ -2426,6 +2428,14 @@
     <string name="satellite_notification_manual_summary" msgid="901206289846283445">"Odesílejte a přijímejte zprávy bez mobilní sítě nebo Wi-Fi"</string>
     <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>
+    <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
+    <skip />
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Čeká na vyřízení…"</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Opětovné nastavení odemknutí otiskem prstu"</string>
     <string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"<xliff:g id="FINGERPRINT">%s</xliff:g> se nedaří rozpoznat."</string>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index 833b0c5..58e9e6f 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -1943,6 +1943,8 @@
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Fra"</string>
     <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
     <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g>-<xliff:g id="END">%2$s</xliff:g>"</string>
+    <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
+    <skip />
     <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Alle kalendere"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> slår nogle lyde fra"</string>
     <string name="system_error_wipe_data" msgid="5910572292172208493">"Der er et internt problem med enheden, og den vil muligvis være ustabil, indtil du gendanner fabriksdataene."</string>
@@ -2424,6 +2426,14 @@
     <string name="satellite_notification_manual_summary" msgid="901206289846283445">"Send og modtag beskeder uden et mobil- eller Wi-Fi-netværk"</string>
     <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>
+    <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
+    <skip />
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Afventer…"</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Konfigurer fingeroplåsning igen"</string>
     <string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"<xliff:g id="FINGERPRINT">%s</xliff:g> kan ikke længere genkendes."</string>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index 1318f64..fa2c9cf 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -1943,6 +1943,8 @@
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Aus"</string>
     <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
     <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g>–<xliff:g id="END">%2$s</xliff:g>"</string>
+    <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
+    <skip />
     <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Alle Kalender"</string>
     <string name="muted_by" msgid="91464083490094950">"Einige Töne werden von <xliff:g id="THIRD_PARTY">%1$s</xliff:g> stummgeschaltet"</string>
     <string name="system_error_wipe_data" msgid="5910572292172208493">"Es liegt ein internes Problem mit deinem Gerät vor. Möglicherweise verhält es sich instabil, bis du es auf die Werkseinstellungen zurücksetzt."</string>
@@ -2424,6 +2426,14 @@
     <string name="satellite_notification_manual_summary" msgid="901206289846283445">"Du kannst ohne Mobilgerät oder WLAN Nachrichten senden und empfangen"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Messages öffnen"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"So funktionierts"</string>
+    <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
+    <skip />
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Ausstehend…"</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Entsperrung per Fingerabdruck neu einrichten"</string>
     <string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"<xliff:g id="FINGERPRINT">%s</xliff:g> wird nicht mehr erkannt."</string>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index bccea8f..9e360e8 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -1405,7 +1405,7 @@
     <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>
-    <string name="adb_active_notification_title" msgid="408390247354560331">"Συνδέθηκε ο εντοπ. σφαλμ. USB"</string>
+    <string name="adb_active_notification_title" msgid="408390247354560331">"Eνεργός εντοπ. σφαλματών USB"</string>
     <string name="adb_active_notification_message" msgid="5617264033476778211">"Πατήστε για απενεργ. εντοπ. σφαλμ. USB"</string>
     <string name="adb_active_notification_message" product="tv" msgid="6624498401272780855">"Επιλογή για απενεργοποίηση του εντοπισμού σφαλμάτων USB."</string>
     <string name="adbwifi_active_notification_title" msgid="6147343659168302473">"Συνδέθηκε ο ασύρματος εντοπισμός σφαλμάτων"</string>
@@ -1943,6 +1943,8 @@
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Ανενεργός"</string>
     <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
     <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> - <xliff:g id="END">%2$s</xliff:g>"</string>
+    <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
+    <skip />
     <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Οποιοδήποτε ημερολόγιο"</string>
     <string name="muted_by" msgid="91464083490094950">"Το τρίτο μέρος <xliff:g id="THIRD_PARTY">%1$s</xliff:g> θέτει ορισμένους ήχους σε σίγαση"</string>
     <string name="system_error_wipe_data" msgid="5910572292172208493">"Υπάρχει ένα εσωτερικό πρόβλημα με τη συσκευή σας και ενδέχεται να είναι ασταθής μέχρι την επαναφορά των εργοστασιακών ρυθμίσεων."</string>
@@ -2424,6 +2426,14 @@
     <string name="satellite_notification_manual_summary" msgid="901206289846283445">"Αποστολή και λήψη μηνυμάτων χωρίς δίκτυο κινητής τηλεφωνίας ή Wi-Fi"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Άνοιγμα Messages"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Πώς λειτουργεί"</string>
+    <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
+    <skip />
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Σε εκκρεμότητα…"</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Επαναρρύθμιση λειτουργίας Ξεκλείδωμα με δακτυλικό αποτύπωμα"</string>
     <string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"Δεν είναι πλέον δυνατή η αναγνώριση του <xliff:g id="FINGERPRINT">%s</xliff:g>."</string>
diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml
index e9239e2..d36ccf5 100644
--- a/core/res/res/values-en-rAU/strings.xml
+++ b/core/res/res/values-en-rAU/strings.xml
@@ -1401,7 +1401,7 @@
     <string name="usb_midi_notification_title" msgid="7404506788950595557">"MIDI via USB turned on"</string>
     <string name="usb_uvc_notification_title" msgid="2030032862673400008">"Device connected as webcam"</string>
     <string name="usb_accessory_notification_title" msgid="1385394660861956980">"USB accessory connected"</string>
-    <string name="usb_notification_message" msgid="4715163067192110676">"Tap for more options."</string>
+    <string name="usb_notification_message" msgid="4715163067192110676">"Tap for more options"</string>
     <string name="usb_power_notification_message" msgid="7284765627437897702">"Charging connected device. Tap for more options."</string>
     <string name="usb_unsupported_audio_accessory_title" msgid="2335775548086533065">"Analogue audio accessory detected"</string>
     <string name="usb_unsupported_audio_accessory_message" msgid="1300168007129796621">"The attached device is not compatible with this phone. Tap to learn more."</string>
@@ -1943,6 +1943,8 @@
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Off"</string>
     <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
     <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g>–<xliff:g id="END">%2$s</xliff:g>"</string>
+    <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
+    <skip />
     <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Any calendar"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> is muting some sounds"</string>
     <string name="system_error_wipe_data" msgid="5910572292172208493">"There\'s an internal problem with your device, and it may be unstable until you factory data reset."</string>
@@ -2424,6 +2426,14 @@
     <string name="satellite_notification_manual_summary" msgid="901206289846283445">"Send and receive messages without a mobile or Wi-Fi network"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Open Messages"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"How it works"</string>
+    <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
+    <skip />
     <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="5851784577768803510">"<xliff:g id="FINGERPRINT">%s</xliff:g> can no longer be recognised."</string>
diff --git a/core/res/res/values-en-rCA/strings.xml b/core/res/res/values-en-rCA/strings.xml
index c624e2a..ef420eb 100644
--- a/core/res/res/values-en-rCA/strings.xml
+++ b/core/res/res/values-en-rCA/strings.xml
@@ -1943,6 +1943,8 @@
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Off"</string>
     <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
     <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> - <xliff:g id="END">%2$s</xliff:g>"</string>
+    <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
+    <skip />
     <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Any calendar"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> is muting some sounds"</string>
     <string name="system_error_wipe_data" msgid="5910572292172208493">"There\'s an internal problem with your device, and it may be unstable until you factory data reset."</string>
@@ -2424,6 +2426,14 @@
     <string name="satellite_notification_manual_summary" msgid="901206289846283445">"Send and receive messages without a mobile or Wi-Fi network"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Open Messages"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"How it works"</string>
+    <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
+    <skip />
     <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="5851784577768803510">"<xliff:g id="FINGERPRINT">%s</xliff:g> can no longer be recognized."</string>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index 9ffaa5d..48f0e1a 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -1401,7 +1401,7 @@
     <string name="usb_midi_notification_title" msgid="7404506788950595557">"MIDI via USB turned on"</string>
     <string name="usb_uvc_notification_title" msgid="2030032862673400008">"Device connected as webcam"</string>
     <string name="usb_accessory_notification_title" msgid="1385394660861956980">"USB accessory connected"</string>
-    <string name="usb_notification_message" msgid="4715163067192110676">"Tap for more options."</string>
+    <string name="usb_notification_message" msgid="4715163067192110676">"Tap for more options"</string>
     <string name="usb_power_notification_message" msgid="7284765627437897702">"Charging connected device. Tap for more options."</string>
     <string name="usb_unsupported_audio_accessory_title" msgid="2335775548086533065">"Analogue audio accessory detected"</string>
     <string name="usb_unsupported_audio_accessory_message" msgid="1300168007129796621">"The attached device is not compatible with this phone. Tap to learn more."</string>
@@ -1943,6 +1943,8 @@
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Off"</string>
     <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
     <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g>–<xliff:g id="END">%2$s</xliff:g>"</string>
+    <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
+    <skip />
     <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Any calendar"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> is muting some sounds"</string>
     <string name="system_error_wipe_data" msgid="5910572292172208493">"There\'s an internal problem with your device, and it may be unstable until you factory data reset."</string>
@@ -2424,6 +2426,14 @@
     <string name="satellite_notification_manual_summary" msgid="901206289846283445">"Send and receive messages without a mobile or Wi-Fi network"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Open Messages"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"How it works"</string>
+    <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
+    <skip />
     <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="5851784577768803510">"<xliff:g id="FINGERPRINT">%s</xliff:g> can no longer be recognised."</string>
diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml
index 23441bb..190e8ab 100644
--- a/core/res/res/values-en-rIN/strings.xml
+++ b/core/res/res/values-en-rIN/strings.xml
@@ -1401,7 +1401,7 @@
     <string name="usb_midi_notification_title" msgid="7404506788950595557">"MIDI via USB turned on"</string>
     <string name="usb_uvc_notification_title" msgid="2030032862673400008">"Device connected as webcam"</string>
     <string name="usb_accessory_notification_title" msgid="1385394660861956980">"USB accessory connected"</string>
-    <string name="usb_notification_message" msgid="4715163067192110676">"Tap for more options."</string>
+    <string name="usb_notification_message" msgid="4715163067192110676">"Tap for more options"</string>
     <string name="usb_power_notification_message" msgid="7284765627437897702">"Charging connected device. Tap for more options."</string>
     <string name="usb_unsupported_audio_accessory_title" msgid="2335775548086533065">"Analogue audio accessory detected"</string>
     <string name="usb_unsupported_audio_accessory_message" msgid="1300168007129796621">"The attached device is not compatible with this phone. Tap to learn more."</string>
@@ -1943,6 +1943,8 @@
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Off"</string>
     <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
     <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g>–<xliff:g id="END">%2$s</xliff:g>"</string>
+    <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
+    <skip />
     <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Any calendar"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> is muting some sounds"</string>
     <string name="system_error_wipe_data" msgid="5910572292172208493">"There\'s an internal problem with your device, and it may be unstable until you factory data reset."</string>
@@ -2424,6 +2426,14 @@
     <string name="satellite_notification_manual_summary" msgid="901206289846283445">"Send and receive messages without a mobile or Wi-Fi network"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Open Messages"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"How it works"</string>
+    <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
+    <skip />
     <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="5851784577768803510">"<xliff:g id="FINGERPRINT">%s</xliff:g> can no longer be recognised."</string>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index 89b22b9..a48f5fa 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -1944,6 +1944,8 @@
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Desactivado"</string>
     <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
     <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> - <xliff:g id="END">%2$s</xliff:g>"</string>
+    <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
+    <skip />
     <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Cualquier calendario"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> silencia algunos sonidos"</string>
     <string name="system_error_wipe_data" msgid="5910572292172208493">"Existe un problema interno con el dispositivo, de modo que el dispositivo puede estar inestable hasta que restablezcas la configuración de fábrica."</string>
@@ -2425,6 +2427,14 @@
     <string name="satellite_notification_manual_summary" msgid="901206289846283445">"Envía y recibe mensajes sin una red móvil ni Wi-Fi"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Abrir Mensajes"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Cómo funciona"</string>
+    <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
+    <skip />
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Pendiente…"</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Vuelve a configurar el Desbloqueo con huellas dactilares"</string>
     <string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"Ya no se puede reconocer <xliff:g id="FINGERPRINT">%s</xliff:g>."</string>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index 0082ade..a573e9d 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -1944,6 +1944,8 @@
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Desactivado"</string>
     <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
     <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g>-<xliff:g id="END">%2$s</xliff:g>"</string>
+    <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
+    <skip />
     <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Cualquier calendario"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> silencia algunos sonidos"</string>
     <string name="system_error_wipe_data" msgid="5910572292172208493">"Se ha producido un problema interno en el dispositivo y es posible que este no sea estable hasta que restablezcas el estado de fábrica."</string>
@@ -2425,6 +2427,14 @@
     <string name="satellite_notification_manual_summary" msgid="901206289846283445">"Envía y recibe mensajes sin una red móvil ni Wi-Fi"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Abre Mensajes"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Cómo funciona"</string>
+    <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
+    <skip />
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Pendiente..."</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Configura Desbloqueo con huella digital de nuevo"</string>
     <string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"<xliff:g id="FINGERPRINT">%s</xliff:g> ya no puede reconocerse."</string>
diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml
index 44f1055..22c7777 100644
--- a/core/res/res/values-et/strings.xml
+++ b/core/res/res/values-et/strings.xml
@@ -1943,6 +1943,8 @@
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Väljas"</string>
     <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
     <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g>–<xliff:g id="END">%2$s</xliff:g>"</string>
+    <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
+    <skip />
     <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Mis tahes kalender"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> vaigistab teatud helid"</string>
     <string name="system_error_wipe_data" msgid="5910572292172208493">"Seadmes ilmnes sisemine probleem ja seade võib olla ebastabiilne seni, kuni lähtestate seadme tehase andmetele."</string>
@@ -2424,6 +2426,14 @@
     <string name="satellite_notification_manual_summary" msgid="901206289846283445">"Sõnumite saatmine ja vastuvõtmine ilma mobiilside- või WiFi-võrguta"</string>
     <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>
+    <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
+    <skip />
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Ootel …"</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Seadistage sõrmejäljega avamine uuesti"</string>
     <string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"Sõrmejälge <xliff:g id="FINGERPRINT">%s</xliff:g> ei saa enam tuvastada."</string>
@@ -2445,7 +2455,7 @@
     <string name="keyboard_shortcut_group_applications_music" msgid="2051507523525651067">"Muusika"</string>
     <string name="keyboard_shortcut_group_applications_calendar" msgid="3571770335653387606">"Kalender"</string>
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"Kalkulaator"</string>
-    <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"Kaardid"</string>
+    <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"Maps"</string>
     <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Rakendused"</string>
     <string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"Teie sõrmejälgi ei saa enam tuvastada. Seadistage sõrmejäljega avamine uuesti."</string>
 </resources>
diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml
index d017a05..8d8ed8f 100644
--- a/core/res/res/values-eu/strings.xml
+++ b/core/res/res/values-eu/strings.xml
@@ -1943,6 +1943,8 @@
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Desaktibatuta"</string>
     <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
     <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g>-<xliff:g id="END">%2$s</xliff:g>"</string>
+    <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
+    <skip />
     <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Edozein egutegi"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> soinu batzuk isilarazten ari da"</string>
     <string name="system_error_wipe_data" msgid="5910572292172208493">"Barneko arazo bat dago zure gailuan eta agian ezegonkor egongo da jatorrizko datuak berrezartzen dituzun arte."</string>
@@ -2424,6 +2426,14 @@
     <string name="satellite_notification_manual_summary" msgid="901206289846283445">"Bidali eta jaso mezuak sare mugikorrik edo wifi-sarerik gabe"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Ireki Mezuak"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Nola funtzionatzen du?"</string>
+    <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
+    <skip />
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Zain…"</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Konfiguratu berriro hatz-marka bidez desblokeatzeko eginbidea"</string>
     <string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"<xliff:g id="FINGERPRINT">%s</xliff:g> ez da ezagutzen jada."</string>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index ad0809f..d6d950c 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -405,7 +405,7 @@
     <string name="permdesc_getTasks" msgid="7388138607018233726">"به برنامه امکان می‌دهد اطلاعات مربوط به کارهای در حال اجرای اخیر و کنونی را بازیابی کند. این ممکن است به برنامه امکان دهد به اطلاعات مربوط به برنامه‌هایی که در دستگاه استفاده می‌شوند دست یابد."</string>
     <string name="permlab_manageProfileAndDeviceOwners" msgid="639849495253987493">"مدیریت نمایه و مالکان دستگاه"</string>
     <string name="permdesc_manageProfileAndDeviceOwners" msgid="7304240671781989283">"به برنامه‌ها امکان می‌دهد، مالکان نمایه و مالک دستگاه را تنظیم کنند."</string>
-    <string name="permlab_reorderTasks" msgid="7598562301992923804">"تنظیم مجدد ترتیب برنامه‌های در حال اجرا"</string>
+    <string name="permlab_reorderTasks" msgid="7598562301992923804">"تغییر ترتیب برنامه‌های درحال اجرا"</string>
     <string name="permdesc_reorderTasks" msgid="8796089937352344183">"‏به برنامه اجازه می‎دهد تا کارها را به پیش‌زمینه و پس‌زمینه منتقل کند. برنامه‎ ممکن است بدون دخالت شما این کار را انجام دهد."</string>
     <string name="permlab_enableCarMode" msgid="893019409519325311">"فعال کردن حالت خودرو"</string>
     <string name="permdesc_enableCarMode" msgid="56419168820473508">"‏به برنامه اجازه می‎دهد تا حالت خودرو را فعال کند."</string>
@@ -463,7 +463,7 @@
     <string name="permdesc_writeSettings" msgid="8293047411196067188">"‏به برنامه اجازه می‎دهد تا داده‎های تنظیم سیستم را تغییر دهد. برنامه‌های مخرب می‎توانند پیکربندی سیستم شما را خراب کنند."</string>
     <string name="permlab_receiveBootCompleted" msgid="6643339400247325379">"اجرا شدن در هنگام راه‌اندازی"</string>
     <string name="permdesc_receiveBootCompleted" product="tablet" msgid="5565659082718177484">"به برنامه اجازه می‌دهد که به محض پایان راه‌اندازی سیستم، راه‌اندازی شود. این ویژگی ممکن است باعث شود راه‌اندازی دستگاه مدت زمان بیشتری طول بکشد و به برنامه اجازه می‌دهد با همیشه درحال اجرا بودنش باعث کاهش سرعت کلی دستگاه شود."</string>
-    <string name="permdesc_receiveBootCompleted" product="tv" msgid="4900842256047614307">"‏به برنامه اجازه می‌دهد به‌محض اتمام راه‌اندازی سیستم، خود را راه‌اندازی کند. ممکن است این مجوز باعث شود دستگاه Android TV آهسته‌تر راه‌اندازی شود و به برنامه اجازه می‌دهد با همیشه درحال اجرا بودن، سرعت کلی دستگاه را کاهش دهد."</string>
+    <string name="permdesc_receiveBootCompleted" product="tv" msgid="4900842256047614307">"‏به برنامه اجازه می‌دهد به‌محض تمام کردن راه‌اندازی سیستم، خود را راه‌اندازی کند. ممکن است این مجوز باعث شود دستگاه Android TV آهسته‌تر راه‌اندازی شود و به برنامه اجازه می‌دهد با همیشه درحال اجرا بودن، سرعت کلی دستگاه را کاهش دهد."</string>
     <string name="permdesc_receiveBootCompleted" product="default" msgid="7912677044558690092">"به برنامه اجازه می‌دهد که به محض پایان راه‌اندازی سیستم، راه‌اندازی شود. این ویژگی ممکن است باعث شود راه‌اندازی دستگاه مدت زمان بیشتری طول بکشد و به برنامه اجازه می‌دهد با همیشه درحال اجرا بودنش باعث کاهش سرعت کلی دستگاه شود."</string>
     <string name="permlab_broadcastSticky" msgid="4552241916400572230">"ارسال پخش چسبنده"</string>
     <string name="permdesc_broadcastSticky" product="tablet" msgid="5058486069846384013">"‏به برنامه اجازه می‎دهد تا پخش‌های ماندگار را که پس از اتمام پخش باقی می‎مانند ارسال کند. استفاده بیش از حد این ویژگی ممکن است باعث مصرف بیش از حد حافظه و در نتیجه کندی یا ناپایداری رایانهٔ لوحی شود."</string>
@@ -1290,9 +1290,9 @@
     <string name="app_upgrading_toast" msgid="1016267296049455585">"<xliff:g id="APPLICATION">%1$s</xliff:g> درحال ارتقا است...."</string>
     <string name="android_preparing_apk" msgid="589736917792300956">"آماده‌سازی <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
     <string name="android_upgrading_starting_apps" msgid="6206161195076057075">"درحال آغاز کردن برنامه‌ها."</string>
-    <string name="android_upgrading_complete" msgid="409800058018374746">"درحال اتمام راه‌اندازی."</string>
+    <string name="android_upgrading_complete" msgid="409800058018374746">"درحال تمام کردن راه‌اندازی."</string>
     <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"دکمه روشن/خاموش را فشار دادید — این کار معمولاً صفحه‌نمایش را خاموش می‌کند.\n\nهنگام راه‌اندازی اثر انگشت، آرام تک‌ضرب بزنید."</string>
-    <string name="fp_power_button_enrollment_title" msgid="6976841690455338563">"برای اتمام راه‌اندازی، صفحه را خاموش کنید"</string>
+    <string name="fp_power_button_enrollment_title" msgid="6976841690455338563">"برای تمام کردن راه‌اندازی، صفحه را خاموش کنید"</string>
     <string name="fp_power_button_enrollment_button_text" msgid="3199783266386029200">"خاموش کردن"</string>
     <string name="fp_power_button_bp_title" msgid="5585506104526820067">"تأیید اثر انگشت را ادامه می‌دهید؟"</string>
     <string name="fp_power_button_bp_message" msgid="2983163038168903393">"دکمه روشن/خاموش را فشار دادید — این کار معمولاً صفحه‌نمایش را خاموش می‌کند.\n\nبرای تأیید اثر انگشتتان، آرام تک‌ضرب بزنید."</string>
@@ -1448,7 +1448,7 @@
     <string name="ext_media_checking_notification_message" product="tv" msgid="7986154434946021415">"درحال تجزیه‌وتحلیل فضای ذخیره‌سازی رسانه"</string>
     <string name="ext_media_new_notification_title" msgid="3517407571407687677">"<xliff:g id="NAME">%s</xliff:g> جدید"</string>
     <string name="ext_media_new_notification_title" product="automotive" msgid="9085349544984742727">"<xliff:g id="NAME">%s</xliff:g> کار نمی‌کند"</string>
-    <string name="ext_media_new_notification_message" msgid="6095403121990786986">"برای راه‌اندازی تک‌ضرب بزنید"</string>
+    <string name="ext_media_new_notification_message" msgid="6095403121990786986">"برای راه‌اندازی، تک‌ضرب بزنید"</string>
     <string name="ext_media_new_notification_message" product="tv" msgid="216863352100263668">"برای راه‌اندازی، انتخاب کنید"</string>
     <string name="ext_media_new_notification_message" product="automotive" msgid="5140127881613227162">"شاید لازم باشد دستگاه را دوباره قالب‌بندی کنید. برای خارج کردن، تک‌ضرب بزنید."</string>
     <string name="ext_media_ready_notification_message" msgid="7509496364380197369">"برای ذخیره کردن عکس، ویدیو، موسیقی و غیره"</string>
@@ -1460,7 +1460,7 @@
     <string name="ext_media_unmountable_notification_message" product="automotive" msgid="2274596120715020680">"شاید لازم باشد دستگاه را دوباره قالب‌بندی کنید. برای خارج کردن، تک‌ضرب بزنید."</string>
     <string name="ext_media_unsupported_notification_title" msgid="3487534182861251401">"<xliff:g id="NAME">%s</xliff:g> تشخیص داده شد"</string>
     <string name="ext_media_unsupported_notification_title" product="automotive" msgid="6004193172658722381">"<xliff:g id="NAME">%s</xliff:g> کار نمی‌کند"</string>
-    <string name="ext_media_unsupported_notification_message" msgid="8463636521459807981">"برای راه‌اندازی تک‌ضرب بزنید."</string>
+    <string name="ext_media_unsupported_notification_message" msgid="8463636521459807981">"برای راه‌اندازی، تک‌ضرب بزنید."</string>
     <string name="ext_media_unsupported_notification_message" product="tv" msgid="1595482802187036532">"برای راه‌اندازی <xliff:g id="NAME">%s</xliff:g> در قالب پشتیبانی‌شده، انتخاب کنید."</string>
     <string name="ext_media_unsupported_notification_message" product="automotive" msgid="3412494732736336330">"شاید لازم باشد دستگاه را دوباره قالب‌بندی کنید"</string>
     <string name="ext_media_badremoval_notification_title" msgid="4114625551266196872">"<xliff:g id="NAME">%s</xliff:g> به‌طور غیرمنتظره جدا شد"</string>
@@ -1943,6 +1943,8 @@
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"خاموش"</string>
     <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">"، "</string>
     <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> - <xliff:g id="END">%2$s</xliff:g>"</string>
+    <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
+    <skip />
     <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"هر تقویمی"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> درحال قطع کردن بعضی از صداهاست"</string>
     <string name="system_error_wipe_data" msgid="5910572292172208493">"دستگاهتان یک مشکل داخلی دارد، و ممکن است تا زمانی که بازنشانی داده‌های کارخانه انجام نگیرد، بی‌ثبات بماند."</string>
@@ -2424,6 +2426,14 @@
     <string name="satellite_notification_manual_summary" msgid="901206289846283445">"‏ارسال و دریافت پیام بدون شبکه تلفن همراه یا Wi-Fi"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"باز کردن «پیام‌نگار»"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"روش کار"</string>
+    <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
+    <skip />
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"درحال تعلیق…"</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"راه‌اندازی مجدد «قفل‌گشایی با اثر انگشت»"</string>
     <string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"‫<xliff:g id="FINGERPRINT">%s</xliff:g> دیگر قابل‌شناسایی نیست."</string>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index 196db12..afc20f6 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -1943,6 +1943,8 @@
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Pois päältä"</string>
     <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
     <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g>–<xliff:g id="END">%2$s</xliff:g>"</string>
+    <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
+    <skip />
     <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Kaikki kalenterit"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> mykistää joitakin ääniä"</string>
     <string name="system_error_wipe_data" msgid="5910572292172208493">"Laitteellasi on sisäinen ongelma, joka aiheuttaa epävakautta. Voit korjata tilanteen palauttamalla tehdasasetukset."</string>
@@ -2424,6 +2426,14 @@
     <string name="satellite_notification_manual_summary" msgid="901206289846283445">"Lähetä ja vastaanota viestejä ilman mobiili- tai Wi-Fi-verkkoa"</string>
     <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>
+    <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
+    <skip />
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Odottaa…"</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Ota sormenjälkiavaus uudelleen käyttöön"</string>
     <string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"<xliff:g id="FINGERPRINT">%s</xliff:g> ei enää ole tunnistettavissa."</string>
diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml
index ac87d3b..6add38a 100644
--- a/core/res/res/values-fr-rCA/strings.xml
+++ b/core/res/res/values-fr-rCA/strings.xml
@@ -1944,6 +1944,8 @@
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Désactivée"</string>
     <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
     <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> – <xliff:g id="END">%2$s</xliff:g>"</string>
+    <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
+    <skip />
     <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"N\'importe quel agenda"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> désactive certains sons"</string>
     <string name="system_error_wipe_data" msgid="5910572292172208493">"Un problème interne est survenu avec votre appareil. Il se peut qu\'il soit instable jusqu\'à ce que vous le réinitialisiez à ses paramètres par défaut."</string>
@@ -2425,6 +2427,14 @@
     <string name="satellite_notification_manual_summary" msgid="901206289846283445">"Envoyez et recevez des messages sans réseau cellulaire ou Wi-Fi"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Ouvrir Messages"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Fonctionnement"</string>
+    <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
+    <skip />
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"En attente…"</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Configurer le Déverrouillage par empreinte digitale à nouveau"</string>
     <string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"L\'empreinte digitale <xliff:g id="FINGERPRINT">%s</xliff:g> ne peut plus être reconnue."</string>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index 2368c88..9de4334 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -1944,6 +1944,8 @@
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Désactivé"</string>
     <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
     <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> – <xliff:g id="END">%2$s</xliff:g>"</string>
+    <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
+    <skip />
     <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Tous les agendas"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> coupe certains sons"</string>
     <string name="system_error_wipe_data" msgid="5910572292172208493">"Un problème interne lié à votre appareil est survenu. Ce dernier risque d\'être instable jusqu\'à ce que vous rétablissiez la configuration d\'usine."</string>
@@ -2425,6 +2427,14 @@
     <string name="satellite_notification_manual_summary" msgid="901206289846283445">"Envoyer et recevoir des messages sans réseau mobile ou Wi-Fi"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Ouvrir Messages"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Fonctionnement"</string>
+    <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
+    <skip />
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"En attente…"</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Reconfigurer le déverrouillage par empreinte digitale"</string>
     <string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"<xliff:g id="FINGERPRINT">%s</xliff:g> ne peut plus être reconnue."</string>
diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml
index 2aba790..06c63ec 100644
--- a/core/res/res/values-gl/strings.xml
+++ b/core/res/res/values-gl/strings.xml
@@ -1943,6 +1943,8 @@
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Desactivada"</string>
     <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
     <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> - <xliff:g id="END">%2$s</xliff:g>"</string>
+    <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
+    <skip />
     <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Calquera calendario"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> está silenciando algúns sons"</string>
     <string name="system_error_wipe_data" msgid="5910572292172208493">"Produciuse un erro interno no teu dispositivo e quizais funcione de maneira inestable ata o restablecemento dos datos de fábrica."</string>
@@ -2424,6 +2426,14 @@
     <string name="satellite_notification_manual_summary" msgid="901206289846283445">"Envía e recibe mensaxes sen ter acceso a redes de telefonía móbil ou wifi"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Abrir Mensaxes"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Como funciona?"</string>
+    <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
+    <skip />
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Pendente..."</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Configura de novo o desbloqueo dactilar"</string>
     <string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"<xliff:g id="FINGERPRINT">%s</xliff:g> xa non se recoñece."</string>
diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml
index 8d90e3b..b0194ca 100644
--- a/core/res/res/values-gu/strings.xml
+++ b/core/res/res/values-gu/strings.xml
@@ -1943,6 +1943,8 @@
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"બંધ છે"</string>
     <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
     <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> - <xliff:g id="END">%2$s</xliff:g>"</string>
+    <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
+    <skip />
     <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"કોઈપણ કૅલેન્ડર"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> અમુક અવાજોને મ્યૂટ કરે છે"</string>
     <string name="system_error_wipe_data" msgid="5910572292172208493">"તમારા ઉપકરણમાં આંતરિક સમસ્યા છે અને જ્યાં સુધી તમે ફેક્ટરી ડેટા ફરીથી સેટ કરશો નહીં ત્યાં સુધી તે અસ્થિર રહી શકે છે."</string>
@@ -2424,6 +2426,14 @@
     <string name="satellite_notification_manual_summary" msgid="901206289846283445">"મોબાઇલ કે વાઇ-ફાઇ નેટવર્ક વિના મેસેજ મોકલો અને મેળવો"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Messages ખોલો"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"તેની કામ કરવાની રીત"</string>
+    <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
+    <skip />
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"બાકી..."</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"ફિંગરપ્રિન્ટ અનલૉક સુવિધાનું ફરી સેટઅપ કરો"</string>
     <string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"હવે <xliff:g id="FINGERPRINT">%s</xliff:g> ઓળખી શકાતી નથી."</string>
@@ -2439,13 +2449,13 @@
     <string name="bg_user_sound_notification_button_mute" msgid="4942158515665615243">"મ્યૂટ કરો"</string>
     <string name="bg_user_sound_notification_message" msgid="8613881975316976673">"સાઉન્ડ મ્યૂટ કરવા માટે ટૅપ કરો"</string>
     <string name="keyboard_shortcut_group_applications_browser" msgid="6535007304687100909">"બ્રાઉઝર"</string>
-    <string name="keyboard_shortcut_group_applications_contacts" msgid="2750702518068326356">"સંપર્કો"</string>
+    <string name="keyboard_shortcut_group_applications_contacts" msgid="2750702518068326356">"Contacts"</string>
     <string name="keyboard_shortcut_group_applications_email" msgid="4229037666415353683">"ઇમેઇલ"</string>
     <string name="keyboard_shortcut_group_applications_sms" msgid="3523799286376321137">"SMS"</string>
     <string name="keyboard_shortcut_group_applications_music" msgid="2051507523525651067">"મ્યુઝિક"</string>
     <string name="keyboard_shortcut_group_applications_calendar" msgid="3571770335653387606">"કૅલેન્ડર"</string>
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"કેલ્ક્યુલેટર"</string>
-    <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"નકશા"</string>
+    <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"Maps"</string>
     <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"ઍપ્લિકેશનો"</string>
     <string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"તમારી ફિંગરપ્રિન્ટને હવેથી ઓળખી શકાશે નહીં. ફિંગરપ્રિન્ટ અનલૉક સુવિધાનું ફરી સેટઅપ કરો."</string>
 </resources>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index 12ffe87..7e27cff 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -329,7 +329,7 @@
     <string name="permgroupdesc_storage" msgid="5378659041354582769">"अपने डिवाइस में मौजूद फ़ाइलों का ऐक्सेस दें"</string>
     <string name="permgrouplab_readMediaAural" msgid="1858331312624942053">"संगीत और ऑडियो"</string>
     <string name="permgroupdesc_readMediaAural" msgid="7565467343667089595">"आपके डिवाइस पर संगीत और ऑडियो को ऐक्सेस करने की अनुमति"</string>
-    <string name="permgrouplab_readMediaVisual" msgid="4724874717811908660">"फ़ोटो और वीडियो के ऐक्सेस"</string>
+    <string name="permgrouplab_readMediaVisual" msgid="4724874717811908660">"फ़ोटो और वीडियो"</string>
     <string name="permgroupdesc_readMediaVisual" msgid="4080463241903508688">"आपके डिवाइस पर फ़ोटो और वीडियो को ऐक्सेस करने की अनुमति"</string>
     <string name="permgrouplab_microphone" msgid="2480597427667420076">"माइक्रोफ़ोन"</string>
     <string name="permgroupdesc_microphone" msgid="1047786732792487722">"ऑडियो रिकॉर्ड करें"</string>
@@ -1943,6 +1943,8 @@
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"बंद है"</string>
     <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
     <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> - <xliff:g id="END">%2$s</xliff:g>"</string>
+    <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
+    <skip />
     <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"कोई भी कैलेंडर"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> कुछ आवाज़ें म्‍यूट कर रहा है"</string>
     <string name="system_error_wipe_data" msgid="5910572292172208493">"आपके डिवाइस में कोई अंदरूनी समस्या है और यह तब तक ठीक नहीं होगी जब तक आप फ़ैक्‍टरी डेटा रीसेट नहीं करते."</string>
@@ -2424,6 +2426,14 @@
     <string name="satellite_notification_manual_summary" msgid="901206289846283445">"मोबाइल या वाई-फ़ाई नेटवर्क के बिना मैसेज भेजें और पाएं"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Messages ऐप्लिकेशन खोलें"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"यह सेटिंग कैसे काम करती है"</string>
+    <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
+    <skip />
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"प्रोसेस जारी है..."</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"फ़िंगरप्रिंट अनलॉक की सुविधा दोबारा सेट अप करें"</string>
     <string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"अब <xliff:g id="FINGERPRINT">%s</xliff:g> की पहचान नहीं की जा सकती."</string>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index 895c3fa..1066559 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -1944,6 +1944,8 @@
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Isključeno"</string>
     <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
     <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> – <xliff:g id="END">%2$s</xliff:g>"</string>
+    <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
+    <skip />
     <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Bilo koji kalendar"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> isključuje neke zvukove"</string>
     <string name="system_error_wipe_data" msgid="5910572292172208493">"Na vašem uređaju postoji interni problem i možda neće biti stabilan dok ga ne vratite na tvorničko stanje."</string>
@@ -2425,6 +2427,14 @@
     <string name="satellite_notification_manual_summary" msgid="901206289846283445">"Šaljite i primajte poruke kad nije dostupna mobilna ili Wi-Fi mreža"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Otvori Poruke"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Kako to funkcionira"</string>
+    <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
+    <skip />
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Na čekanju..."</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Ponovno postavite otključavanje otiskom prsta"</string>
     <string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"<xliff:g id="FINGERPRINT">%s</xliff:g> više se ne prepoznaje."</string>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index 3bbaef2..c76d9f4 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -1943,6 +1943,8 @@
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Kikapcsolva"</string>
     <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
     <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> – <xliff:g id="END">%2$s</xliff:g>"</string>
+    <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
+    <skip />
     <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Bármilyen naptár"</string>
     <string name="muted_by" msgid="91464083490094950">"A(z) <xliff:g id="THIRD_PARTY">%1$s</xliff:g> lenémít néhány hangot"</string>
     <string name="system_error_wipe_data" msgid="5910572292172208493">"Belső probléma van az eszközzel, és instabil lehet, amíg vissza nem állítja a gyári adatokat."</string>
@@ -2424,6 +2426,14 @@
     <string name="satellite_notification_manual_summary" msgid="901206289846283445">"Küldhet és fogadhat üzeneteket mobil- és Wi-Fi-hálózat nélkül is"</string>
     <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>
+    <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
+    <skip />
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Függőben…"</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"A Feloldás ujjlenyomattal funkció újbóli beállítása"</string>
     <string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"A következő már nem felismerhető: <xliff:g id="FINGERPRINT">%s</xliff:g>."</string>
diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml
index d341adaf..c91918d 100644
--- a/core/res/res/values-hy/strings.xml
+++ b/core/res/res/values-hy/strings.xml
@@ -1943,6 +1943,8 @@
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Անջատված է"</string>
     <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
     <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g>–<xliff:g id="END">%2$s</xliff:g>"</string>
+    <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
+    <skip />
     <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Ցանկացած օրացույց"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g>-ն անջատում է որոշ ձայներ"</string>
     <string name="system_error_wipe_data" msgid="5910572292172208493">"Սարքում ներքին խնդիր է առաջացել և այն կարող է կրկնվել, մինչև չվերականգնեք գործարանային կարգավորումները:"</string>
@@ -2424,6 +2426,14 @@
     <string name="satellite_notification_manual_summary" msgid="901206289846283445">"Ուղարկեք և ստացեք հաղորդագրություններ առանց բջջային կամ Wi-Fi ցանցի"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Բացել Messages-ը"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Ինչպես է դա աշխատում"</string>
+    <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
+    <skip />
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Առկախ է…"</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Նորից կարգավորեք մատնահետքով ապակողպումը"</string>
     <string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"<xliff:g id="FINGERPRINT">%s</xliff:g> մատնահետքն այլևս չի կարող ճանաչվել։"</string>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index 957e835..4984630 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -1943,6 +1943,8 @@
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Nonaktif"</string>
     <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
     <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> - <xliff:g id="END">%2$s</xliff:g>"</string>
+    <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
+    <skip />
     <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Kalender mana saja"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> mematikan beberapa suara"</string>
     <string name="system_error_wipe_data" msgid="5910572292172208493">"Ada masalah dengan perangkat. Hal ini mungkin membuat perangkat jadi tidak stabil dan perlu dikembalikan ke setelan pabrik."</string>
@@ -2424,6 +2426,14 @@
     <string name="satellite_notification_manual_summary" msgid="901206289846283445">"Mengirim dan menerima pesan tanpa jaringan seluler atau Wi-Fi"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Buka Message"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Cara kerjanya"</string>
+    <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
+    <skip />
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Tertunda..."</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Siapkan Buka dengan Sidik Jari lagi"</string>
     <string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"<xliff:g id="FINGERPRINT">%s</xliff:g> tidak dapat dikenali lagi."</string>
diff --git a/core/res/res/values-is/strings.xml b/core/res/res/values-is/strings.xml
index b6d1c12..47a6c35 100644
--- a/core/res/res/values-is/strings.xml
+++ b/core/res/res/values-is/strings.xml
@@ -1943,6 +1943,8 @@
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Slökkt"</string>
     <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
     <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> - <xliff:g id="END">%2$s</xliff:g>"</string>
+    <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
+    <skip />
     <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Öll dagatöl"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> þaggar í einhverjum hljóðum"</string>
     <string name="system_error_wipe_data" msgid="5910572292172208493">"Innra vandamál kom upp í tækinu og það kann að vera óstöðugt þangað til þú núllstillir það."</string>
@@ -2424,6 +2426,14 @@
     <string name="satellite_notification_manual_summary" msgid="901206289846283445">"Senda og fá skilaboð án tengingar við farsímakerfi eða Wi-Fi-net"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Opna Messages"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Svona virkar þetta"</string>
+    <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
+    <skip />
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Í bið…"</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Setja upp fingrafarskenni aftur"</string>
     <string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"Ekki er hægt að bera kennsl á <xliff:g id="FINGERPRINT">%s</xliff:g> lengur."</string>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index 66e5f72..051c61a 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -1402,7 +1402,7 @@
     <string name="usb_midi_notification_title" msgid="7404506788950595557">"Modalità MIDI tramite USB attivata"</string>
     <string name="usb_uvc_notification_title" msgid="2030032862673400008">"Dispositivo connesso come webcam"</string>
     <string name="usb_accessory_notification_title" msgid="1385394660861956980">"Accessorio USB collegato"</string>
-    <string name="usb_notification_message" msgid="4715163067192110676">"Tocca per altre opzioni."</string>
+    <string name="usb_notification_message" msgid="4715163067192110676">"Tocca per altre opzioni"</string>
     <string name="usb_power_notification_message" msgid="7284765627437897702">"Dispositivo collegato in carica. Tocca per altre opzioni."</string>
     <string name="usb_unsupported_audio_accessory_title" msgid="2335775548086533065">"Accessorio audio analogico rilevato"</string>
     <string name="usb_unsupported_audio_accessory_message" msgid="1300168007129796621">"Il dispositivo collegato non è compatibile con questo telefono. Tocca per avere ulteriori informazioni."</string>
@@ -1944,6 +1944,8 @@
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Off"</string>
     <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
     <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> - <xliff:g id="END">%2$s</xliff:g>"</string>
+    <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
+    <skip />
     <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Qualsiasi calendario"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> sta disattivando alcuni suoni"</string>
     <string name="system_error_wipe_data" msgid="5910572292172208493">"Si è verificato un problema interno con il dispositivo, che potrebbe essere instabile fino al ripristino dei dati di fabbrica."</string>
@@ -2425,6 +2427,14 @@
     <string name="satellite_notification_manual_summary" msgid="901206289846283445">"Invia e ricevi messaggi senza una rete mobile o Wi-Fi"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Apri Messaggi"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Come funziona"</string>
+    <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
+    <skip />
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"In attesa…"</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Riconfigura lo Sblocco con l\'Impronta"</string>
     <string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"<xliff:g id="FINGERPRINT">%s</xliff:g> non può più essere riconosciuto."</string>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index 9323276..8b0dd80 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -1402,7 +1402,7 @@
     <string name="usb_midi_notification_title" msgid="7404506788950595557">"‏MIDI באמצעות USB מופעל"</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>
@@ -1944,6 +1944,8 @@
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"מצב מושבת"</string>
     <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
     <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"‫<xliff:g id="START">%1$s</xliff:g> – <xliff:g id="END">%2$s</xliff:g>"</string>
+    <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
+    <skip />
     <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"כל יומן"</string>
     <string name="muted_by" msgid="91464083490094950">"חלק מהצלילים מושתקים על ידי <xliff:g id="THIRD_PARTY">%1$s</xliff:g>"</string>
     <string name="system_error_wipe_data" msgid="5910572292172208493">"קיימת בעיה פנימית במכשיר שלך, וייתכן שהוא לא יתפקד כראוי עד שיבוצע איפוס לנתוני היצרן."</string>
@@ -2425,6 +2427,14 @@
     <string name="satellite_notification_manual_summary" msgid="901206289846283445">"‏אפשר לשלוח ולקבל הודעות ללא רשת סלולרית או Wi-Fi"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"‏לפתיחת Messages"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"איך זה עובד"</string>
+    <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
+    <skip />
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"בהמתנה..."</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"הגדרה חוזרת של \'ביטול הנעילה בטביעת אצבע\'"</string>
     <string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"כבר לא ניתן לזהות את <xliff:g id="FINGERPRINT">%s</xliff:g>."</string>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index cd5527c..2f38929 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -1943,6 +1943,8 @@
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"OFF"</string>
     <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">"、 "</string>
     <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g>~<xliff:g id="END">%2$s</xliff:g>"</string>
+    <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
+    <skip />
     <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"すべてのカレンダー"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> により一部の音はミュートに設定"</string>
     <string name="system_error_wipe_data" msgid="5910572292172208493">"デバイスで内部的な問題が発生しました。データが初期化されるまで不安定になる可能性があります。"</string>
@@ -2424,6 +2426,14 @@
     <string name="satellite_notification_manual_summary" msgid="901206289846283445">"モバイル ネットワークや Wi-Fi ネットワークがなくてもメッセージを送受信できます"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"メッセージ アプリを開く"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"仕組み"</string>
+    <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
+    <skip />
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"保留中..."</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"指紋認証をもう一度設定してください"</string>
     <string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"<xliff:g id="FINGERPRINT">%s</xliff:g>を認識できなくなりました。"</string>
diff --git a/core/res/res/values-ka/strings.xml b/core/res/res/values-ka/strings.xml
index 9b1c07f..2d9aecd 100644
--- a/core/res/res/values-ka/strings.xml
+++ b/core/res/res/values-ka/strings.xml
@@ -1406,7 +1406,7 @@
     <string name="usb_unsupported_audio_accessory_title" msgid="2335775548086533065">"აღმოჩენილია ანალოგური აუდიო აქსესუარი"</string>
     <string name="usb_unsupported_audio_accessory_message" msgid="1300168007129796621">"მიერთებული მოწყობილობა არაა თავსებადი ამ ტელეფონთან. მეტის გასაგებად, შეეხეთ."</string>
     <string name="adb_active_notification_title" msgid="408390247354560331">"USB გამართვა შეერთებულია"</string>
-    <string name="adb_active_notification_message" msgid="5617264033476778211">"შეეხეთ და გამორთეთ USB გამართვა"</string>
+    <string name="adb_active_notification_message" msgid="5617264033476778211">"შეეხეთ USB გამართვის გამოსართავად"</string>
     <string name="adb_active_notification_message" product="tv" msgid="6624498401272780855">"მონიშნეთ რათა შეწყვიტოთ USB-ის გამართვა"</string>
     <string name="adbwifi_active_notification_title" msgid="6147343659168302473">"შეცდომების უსადენო გამართვა დაკავშირებულია"</string>
     <string name="adbwifi_active_notification_message" msgid="930987922852867972">"შეეხეთ შეცდომების უსადენო გამართვის გამოსართავად"</string>
@@ -1943,6 +1943,8 @@
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"გამორთული"</string>
     <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
     <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> – <xliff:g id="END">%2$s</xliff:g>"</string>
+    <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
+    <skip />
     <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"ნებისმიერი კალენდარი"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> ზოგიერთ ხმას ადუმებს"</string>
     <string name="system_error_wipe_data" msgid="5910572292172208493">"ფიქსირდება თქვენი მ ოწყობილობის შიდა პრობლემა და შეიძლება არასტაბილური იყოს, სანამ ქარხნულ მონაცემების არ განაახლებთ."</string>
@@ -2424,6 +2426,14 @@
     <string name="satellite_notification_manual_summary" msgid="901206289846283445">"შეტყობინებების გაგზავნა და მიღება მობილური ან Wi-Fi ქსელის გარეშე"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Messages-ის გახსნა"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"მუშაობის პრინციპი"</string>
+    <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
+    <skip />
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"მომლოდინე..."</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"ანაბეჭდით განბლოკვის ხელახლა დაყენება"</string>
     <string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"<xliff:g id="FINGERPRINT">%s</xliff:g>-ის ამოცნობა ვეღარ ხერხდება."</string>
diff --git a/core/res/res/values-kk/strings.xml b/core/res/res/values-kk/strings.xml
index ebf627f..6986a7b 100644
--- a/core/res/res/values-kk/strings.xml
+++ b/core/res/res/values-kk/strings.xml
@@ -1943,6 +1943,8 @@
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Өшірулі"</string>
     <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
     <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> – <xliff:g id="END">%2$s</xliff:g>"</string>
+    <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
+    <skip />
     <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Кез келген күнтізбе"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> кейбір дыбыстарды өшіруде"</string>
     <string name="system_error_wipe_data" msgid="5910572292172208493">"There\'s an internal problem with your device, and it may be unstable until you factory data reset."</string>
@@ -2424,6 +2426,14 @@
     <string name="satellite_notification_manual_summary" msgid="901206289846283445">"Хабарландыруларды мобильдік желіге немесе Wi-Fi желісіне қосылмай жіберіңіз және алыңыз."</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Messages қолданбасын ашу"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Бұл қалай орындалады?"</string>
+    <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
+    <skip />
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Дайын емес…"</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Саусақ ізімен ашу функциясын қайта реттеу"</string>
     <string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"<xliff:g id="FINGERPRINT">%s</xliff:g> бұдан былай танылмайды."</string>
diff --git a/core/res/res/values-km/strings.xml b/core/res/res/values-km/strings.xml
index 41f2de0..b2bb78b 100644
--- a/core/res/res/values-km/strings.xml
+++ b/core/res/res/values-km/strings.xml
@@ -1943,6 +1943,8 @@
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"បិទ"</string>
     <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
     <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> - <xliff:g id="END">%2$s</xliff:g>"</string>
+    <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
+    <skip />
     <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"ប្រតិទិនណាមួយ"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> កំពុង​បិទសំឡេង​មួយចំនួន"</string>
     <string name="system_error_wipe_data" msgid="5910572292172208493">"មានបញ្ហាខាងក្នុងឧបករណ៍របស់អ្នក ហើយវាអ្នកមិនមានស្ថេរភាព រហូតទាល់តែអ្នកកំណត់ដូចដើមវិញទាំងស្រុង។"</string>
@@ -2424,6 +2426,14 @@
     <string name="satellite_notification_manual_summary" msgid="901206289846283445">"ផ្ញើ និងទទួលសារដោយគ្មានបណ្ដាញ Wi-Fi ឬបណ្ដាញទូរសព្ទចល័ត"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"បើក​កម្មវិធី Messages"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"របៀបដែលវាដំណើរការ"</string>
+    <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
+    <skip />
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"កំពុងរង់ចាំ..."</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"រៀបចំការដោះសោ​ដោយស្កេន​ស្នាមម្រាមដៃម្ដងទៀត"</string>
     <string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"លែងអាចសម្គាល់ <xliff:g id="FINGERPRINT">%s</xliff:g> បានទៀតហើយ។"</string>
diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml
index 5ddb586..8332cfc 100644
--- a/core/res/res/values-kn/strings.xml
+++ b/core/res/res/values-kn/strings.xml
@@ -1943,6 +1943,8 @@
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"ಆಫ್ ಆಗಿದೆ"</string>
     <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
     <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> - <xliff:g id="END">%2$s</xliff:g>"</string>
+    <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
+    <skip />
     <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"ಯಾವುದೇ ಕ್ಯಾಲೆಂಡರ್"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> ಧ್ವನಿ ಮ್ಯೂಟ್ ಮಾಡುತ್ತಿದ್ದಾರೆ"</string>
     <string name="system_error_wipe_data" msgid="5910572292172208493">"ನಿಮ್ಮ ಸಾಧನದಲ್ಲಿ ಆಂತರಿಕ ಸಮಸ್ಯೆಯಿದೆ ಹಾಗೂ ನೀವು ಫ್ಯಾಕ್ಟರಿ ಡೇಟಾವನ್ನು ರೀಸೆಟ್ ಮಾಡುವವರೆಗೂ ಅದು ಅಸ್ಥಿರವಾಗಬಹುದು."</string>
@@ -2424,6 +2426,14 @@
     <string name="satellite_notification_manual_summary" msgid="901206289846283445">"ಮೊಬೈಲ್ ಅಥವಾ ವೈ-ಫೈ ನೆಟ್‌ವರ್ಕ್ ಇಲ್ಲದೆಯೇ ಸಂದೇಶಗಳನ್ನು ಕಳುಹಿಸಿ ಮತ್ತು ಸ್ವೀಕರಿಸಿ"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Messages ಅನ್ನು ತೆರೆಯಿರಿ"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"ಇದು ಹೇಗೆ ಕೆಲಸ ಮಾಡುತ್ತದೆ"</string>
+    <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
+    <skip />
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"ಬಾಕಿ ಉಳಿದಿದೆ..."</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"ಫಿಂಗರ್‌ಪ್ರಿಂಟ್ ಅನ್‌ಲಾಕ್ ಅನ್ನು ಮತ್ತೊಮ್ಮೆ ಸೆಟಪ್ ಮಾಡಿ"</string>
     <string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"<xliff:g id="FINGERPRINT">%s</xliff:g> ಅನ್ನು ಇನ್ನು ಮುಂದೆ ಗುರುತಿಸಲಾಗುವುದಿಲ್ಲ."</string>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index 92ca1d5..393c956 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -1943,6 +1943,8 @@
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"사용 중지"</string>
     <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
     <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g>~<xliff:g id="END">%2$s</xliff:g>"</string>
+    <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
+    <skip />
     <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"모든 캘린더"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g>(이)가 일부 소리를 음소거함"</string>
     <string name="system_error_wipe_data" msgid="5910572292172208493">"사용 중인 기기 내부에 문제가 발생했습니다. 초기화할 때까지 불안정할 수 있습니다."</string>
@@ -2424,6 +2426,14 @@
     <string name="satellite_notification_manual_summary" msgid="901206289846283445">"모바일 또는 Wi-Fi 네트워크 없이 메시지 주고받기"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"메시지 열기"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"작동 방식"</string>
+    <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
+    <skip />
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"대기 중…"</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"지문 잠금 해제 다시 설정"</string>
     <string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"<xliff:g id="FINGERPRINT">%s</xliff:g> 지문을 더 이상 인식할 수 없습니다."</string>
diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml
index a4380253..e44538b 100644
--- a/core/res/res/values-ky/strings.xml
+++ b/core/res/res/values-ky/strings.xml
@@ -1943,6 +1943,8 @@
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Өчүк"</string>
     <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
     <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g>, <xliff:g id="END">%2$s</xliff:g>"</string>
+    <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
+    <skip />
     <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Бардык жылнаамалар"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> айрым үндөрдү өчүрүүдө"</string>
     <string name="system_error_wipe_data" msgid="5910572292172208493">"Түзмөгүңүздө ички көйгөй бар жана ал баштапкы абалга кайтарылмайынча туруктуу иштебей коюшу мүмкүн."</string>
@@ -2424,6 +2426,14 @@
     <string name="satellite_notification_manual_summary" msgid="901206289846283445">"Мобилдик же Wi-Fi тармагына туташпай эле билдирүүлөрдү жөнөтүп, алыңыз"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Жазышуулар колдонмосун ачуу"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Ал кантип иштейт"</string>
+    <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
+    <skip />
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Кезекте турат..."</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Манжа изи менен ачуу функциясын кайра тууралаңыз"</string>
     <string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"<xliff:g id="FINGERPRINT">%s</xliff:g> мындан ары таанылбайт."</string>
diff --git a/core/res/res/values-lo/strings.xml b/core/res/res/values-lo/strings.xml
index be09c10..15138bb 100644
--- a/core/res/res/values-lo/strings.xml
+++ b/core/res/res/values-lo/strings.xml
@@ -1943,6 +1943,8 @@
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"ປິດຢູ່"</string>
     <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
     <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> - <xliff:g id="END">%2$s</xliff:g>"</string>
+    <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
+    <skip />
     <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"ປະ​ຕິ​ທິນ​ໃດ​ກໍໄດ້"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> ປິດສຽງບາງຢ່າງໄວ້"</string>
     <string name="system_error_wipe_data" msgid="5910572292172208493">"ມີ​ບັນ​ຫາ​ພາຍ​ໃນ​ກັບ​ອຸ​ປະ​ກອນ​ຂອງ​ທ່ານ, ແລະ​ມັນ​ອາດ​ຈະ​ບໍ່​ສະ​ຖຽນ​ຈົນ​ກວ່າ​ທ່ານ​ຕັ້ງ​ເປັນ​ຂໍ້​ມູນ​ໂຮງ​ງານ​ຄືນ​ແລ້ວ."</string>
@@ -2424,6 +2426,14 @@
     <string name="satellite_notification_manual_summary" msgid="901206289846283445">"ຮັບ ແລະ ສົ່ງຂໍ້ຄວາມໂດຍບໍ່ຕ້ອງໃຊ້ເຄືອຂ່າຍໂທລະສັບມືຖື ຫຼື Wi-Fi"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"ເປີດ Messages"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"ມັນເຮັດວຽກແນວໃດ"</string>
+    <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
+    <skip />
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"ລໍຖ້າດຳເນີນການ..."</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"ຕັ້ງຄ່າການປົດລັອກດ້ວຍລາຍນິ້ວມືຄືນໃໝ່"</string>
     <string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"ບໍ່ສາມາດຈຳແນກ <xliff:g id="FINGERPRINT">%s</xliff:g> ໄດ້ອີກຕໍ່ໄປ."</string>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index 45998ee..51aa8a1 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -1945,6 +1945,8 @@
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Išjungti"</string>
     <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
     <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g>–<xliff:g id="END">%2$s</xliff:g>"</string>
+    <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
+    <skip />
     <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Bet kuris kalendorius"</string>
     <string name="muted_by" msgid="91464083490094950">"„<xliff:g id="THIRD_PARTY">%1$s</xliff:g>“ nutildo kai kuriuos garsus"</string>
     <string name="system_error_wipe_data" msgid="5910572292172208493">"Iškilo vidinė su jūsų įrenginiu susijusi problema, todėl įrenginys gali veikti nestabiliai, kol neatkursite gamyklinių duomenų."</string>
@@ -2426,6 +2428,14 @@
     <string name="satellite_notification_manual_summary" msgid="901206289846283445">"Siųskite ir gaukite pranešimus be mobiliojo ryšio ar „Wi-Fi“ tinklo"</string>
     <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>
+    <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
+    <skip />
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Laukiama..."</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Atrakinimo piršto atspaudu nustatymas dar kartą"</string>
     <string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"<xliff:g id="FINGERPRINT">%s</xliff:g> nebegalima atpažinti."</string>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index 973d8e2..43de9f5 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -1944,6 +1944,8 @@
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Izslēgta"</string>
     <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
     <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g>–<xliff:g id="END">%2$s</xliff:g>"</string>
+    <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
+    <skip />
     <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Jebkurš kalendārs"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> izslēdz noteiktas skaņas"</string>
     <string name="system_error_wipe_data" msgid="5910572292172208493">"Jūsu ierīcē ir radusies iekšēja problēma, un ierīce var darboties nestabili. Lai to labotu, veiciet rūpnīcas datu atiestatīšanu."</string>
@@ -2425,6 +2427,14 @@
     <string name="satellite_notification_manual_summary" msgid="901206289846283445">"Sūtiet un saņemiet ziņojumus bez mobilā vai Wi‑Fi tīkla."</string>
     <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>
+    <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
+    <skip />
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Gaida…"</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Vēlreiz iestatiet autorizāciju ar pirksta nospiedumu"</string>
     <string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"Pirksta nospiedumu (<xliff:g id="FINGERPRINT">%s</xliff:g>) vairs nevar atpazīt."</string>
diff --git a/core/res/res/values-mk/strings.xml b/core/res/res/values-mk/strings.xml
index 3943dc3..ea38037 100644
--- a/core/res/res/values-mk/strings.xml
+++ b/core/res/res/values-mk/strings.xml
@@ -1943,6 +1943,8 @@
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Исклучено"</string>
     <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
     <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> – <xliff:g id="END">%2$s</xliff:g>"</string>
+    <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
+    <skip />
     <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Кој било календар"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> исклучи некои звуци"</string>
     <string name="system_error_wipe_data" msgid="5910572292172208493">"Настана внатрешен проблем со уредот и може да биде нестабилен сè додека не ресетирате на фабричките податоци."</string>
@@ -2424,6 +2426,14 @@
     <string name="satellite_notification_manual_summary" msgid="901206289846283445">"Испраќајте и примајте пораки без мобилна или Wi-Fi мрежа"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Отворете ја Messages"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Дознајте како функционира"</string>
+    <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
+    <skip />
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Во фаза на чекање…"</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Поставете „Отклучување со отпечаток“ повторно"</string>
     <string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"<xliff:g id="FINGERPRINT">%s</xliff:g> веќе не може да се препознае."</string>
diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml
index 242e967..ae6505d 100644
--- a/core/res/res/values-ml/strings.xml
+++ b/core/res/res/values-ml/strings.xml
@@ -1943,6 +1943,8 @@
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"ഓഫാണ്"</string>
     <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
     <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> - <xliff:g id="END">%2$s</xliff:g>"</string>
+    <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
+    <skip />
     <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"എല്ലാ കലണ്ടറിലും"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> ചില ശബ്‌ദങ്ങൾ മ്യൂട്ട് ചെയ്യുന്നു"</string>
     <string name="system_error_wipe_data" msgid="5910572292172208493">"നിങ്ങളുടെ ഉപകരണത്തിൽ ഒരു ആന്തരിക പ്രശ്‌നമുണ്ട്, ഫാക്‌ടറി വിവര പുനഃസജ്ജീകരണം ചെയ്യുന്നതുവരെ ഇതു അസ്ഥിരമായിരിക്കാനിടയുണ്ട്."</string>
@@ -2424,6 +2426,14 @@
     <string name="satellite_notification_manual_summary" msgid="901206289846283445">"മൊബൈൽ അല്ലെങ്കിൽ വൈഫൈ നെറ്റ്‌വർക്ക് ഇല്ലാതെ സന്ദേശങ്ങൾ അയയ്ക്കുകയും സ്വീകരിക്കുകയും ചെയ്യുക"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Messages തുറക്കുക"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"ഇത് പ്രവർത്തിക്കുന്നത് എങ്ങനെയാണ്"</string>
+    <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
+    <skip />
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"തീർപ്പാക്കിയിട്ടില്ല..."</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"ഫിംഗർപ്രിന്റ് അൺലോക്ക് വീണ്ടും സജ്ജീകരിക്കുക"</string>
     <string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"<xliff:g id="FINGERPRINT">%s</xliff:g> ഇനി തിരിച്ചറിയാനാകില്ല."</string>
@@ -2445,7 +2455,7 @@
     <string name="keyboard_shortcut_group_applications_music" msgid="2051507523525651067">"സംഗീതം"</string>
     <string name="keyboard_shortcut_group_applications_calendar" msgid="3571770335653387606">"കലണ്ടർ"</string>
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"കാൽക്കുലേറ്റർ"</string>
-    <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"മാപ്പുകൾ"</string>
+    <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"Maps"</string>
     <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"ആപ്പുകൾ"</string>
     <string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"നിങ്ങളുടെ ഫിംഗർപ്രിന്റുകൾ ഇനി തിരിച്ചറിയാനാകില്ല. ഫിംഗർപ്രിന്റ് അൺലോക്ക് വീണ്ടും സജ്ജീകരിക്കുക."</string>
 </resources>
diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml
index c8f2eaa..3d2eb04 100644
--- a/core/res/res/values-mn/strings.xml
+++ b/core/res/res/values-mn/strings.xml
@@ -1943,6 +1943,8 @@
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Унтраалттай"</string>
     <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
     <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> - <xliff:g id="END">%2$s</xliff:g>"</string>
+    <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
+    <skip />
     <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Дурын календарь"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> зарим дууны дууг хааж байна"</string>
     <string name="system_error_wipe_data" msgid="5910572292172208493">"Таны төхөөрөмжид дотоод алдаа байна.Та төхөөрөмжөө үйлдвэрээс гарсан төлөвт шилжүүлэх хүртэл таны төхөөрөмж чинь тогтворгүй байж болох юм."</string>
@@ -2424,6 +2426,14 @@
     <string name="satellite_notification_manual_summary" msgid="901206289846283445">"Хөдөлгөөнт холбооны эсвэл Wi-Fi сүлжээгүйгээр мессеж илгээх болон хүлээн авах"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Мессежийг нээх"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Энэ хэрхэн ажилладаг вэ?"</string>
+    <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
+    <skip />
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Хүлээгдэж буй..."</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Хурууны хээгээр түгжээ тайлахыг дахин тохируулна уу"</string>
     <string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"<xliff:g id="FINGERPRINT">%s</xliff:g>-г цаашид таних боломжгүй."</string>
diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml
index 9980d9f..9243f47 100644
--- a/core/res/res/values-mr/strings.xml
+++ b/core/res/res/values-mr/strings.xml
@@ -1943,6 +1943,8 @@
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"बंद आहे"</string>
     <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
     <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> - <xliff:g id="END">%2$s</xliff:g>"</string>
+    <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
+    <skip />
     <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"कोणतेही कॅलेंडर"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> काही ध्‍वनी म्‍यूट करत आहे"</string>
     <string name="system_error_wipe_data" msgid="5910572292172208493">"आपल्‍या डिव्‍हाइसमध्‍ये अंतर्गत समस्‍या आहे आणि तुमचा फॅक्‍टरी डेटा रीसेट होईपर्यंत ती अस्‍थिर असू शकते."</string>
@@ -2424,6 +2426,14 @@
     <string name="satellite_notification_manual_summary" msgid="901206289846283445">"मोबाइल किंवा वाय-फाय नेटवर्कशिवाय मेसेज पाठवणे आणि मिळवणे"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Messages उघडा"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"ते कसे काम करते"</string>
+    <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
+    <skip />
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"प्रलंबित आहे..."</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"फिंगरप्रिंट अनलॉक पुन्हा सेट करा"</string>
     <string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"<xliff:g id="FINGERPRINT">%s</xliff:g> यापुढे ओळखता येणार नाही."</string>
diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml
index e811635..9bda173 100644
--- a/core/res/res/values-ms/strings.xml
+++ b/core/res/res/values-ms/strings.xml
@@ -1943,6 +1943,8 @@
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Mati"</string>
     <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
     <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> - <xliff:g id="END">%2$s</xliff:g>"</string>
+    <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
+    <skip />
     <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Sebarang kalendar"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> meredamkan sesetengah bunyi"</string>
     <string name="system_error_wipe_data" msgid="5910572292172208493">"Terdapat masalah dalaman dengan peranti anda. Peranti mungkin tidak stabil sehingga anda membuat tetapan semula data kilang."</string>
@@ -2414,7 +2416,7 @@
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Ruang persendirian"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klon"</string>
     <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Umum"</string>
-    <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Ruang privasi"</string>
+    <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Ruang persendirian"</string>
     <string name="redacted_notification_message" msgid="1520587845842228816">"Kandungan pemberitahuan yang sensitif disembunyikan"</string>
     <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Kandungan apl disembunyikan daripada perkongsian skrin untuk keselamatan"</string>
@@ -2424,6 +2426,14 @@
     <string name="satellite_notification_manual_summary" msgid="901206289846283445">"Hantar dan terima mesej tanpa rangkaian mudah alih atau Wi-Fi"</string>
     <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>
+    <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
+    <skip />
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Belum selesai..."</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Sediakan Buka Kunci Cap Jari sekali lagi"</string>
     <string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"<xliff:g id="FINGERPRINT">%s</xliff:g> tidak dapat dicam lagi."</string>
diff --git a/core/res/res/values-my/strings.xml b/core/res/res/values-my/strings.xml
index 7088a15..2581a74 100644
--- a/core/res/res/values-my/strings.xml
+++ b/core/res/res/values-my/strings.xml
@@ -1943,6 +1943,8 @@
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"ပိတ်"</string>
     <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">"၊ "</string>
     <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> - <xliff:g id="END">%2$s</xliff:g>"</string>
+    <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
+    <skip />
     <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"မည်သည့်ပြက္ခဒိန်မဆို"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> သည် အချို့အသံကို ပိတ်နေသည်"</string>
     <string name="system_error_wipe_data" msgid="5910572292172208493">"သင့်ကိရိယာအတွင်းပိုင်းတွင် ပြဿနာရှိနေပြီး၊ မူလစက်ရုံထုတ်အခြေအနေအဖြစ် ပြန်လည်ရယူနိုင်သည်အထိ အခြေအနေမတည်ငြိမ်နိုင်ပါ။"</string>
@@ -2424,6 +2426,14 @@
     <string name="satellite_notification_manual_summary" msgid="901206289846283445">"မိုဘိုင်း (သို့) Wi-Fi ကွန်ရက်မရှိဘဲ မက်ဆေ့ဂျ်များ ပို့နိုင်၊ လက်ခံနိုင်သည်"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Messages ဖွင့်ရန်"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"အလုပ်လုပ်ပုံ"</string>
+    <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
+    <skip />
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"ဆိုင်းငံ့ထားသည်…"</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"‘လက်ဗွေသုံး လော့ခ်ဖွင့်ခြင်း’ ကို စနစ်ထပ်မံထည့်သွင်းပါ"</string>
     <string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"<xliff:g id="FINGERPRINT">%s</xliff:g> ကို မသိရှိနိုင်တော့ပါ။"</string>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index f39a4f8..2228864 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -1943,6 +1943,8 @@
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Av"</string>
     <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
     <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> – <xliff:g id="END">%2$s</xliff:g>"</string>
+    <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
+    <skip />
     <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Hvilken som helst kalender"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> slår av noen lyder"</string>
     <string name="system_error_wipe_data" msgid="5910572292172208493">"Det har oppstått et internt problem på enheten din, og den kan være ustabil til du tilbakestiller den til fabrikkdata."</string>
@@ -2424,6 +2426,14 @@
     <string name="satellite_notification_manual_summary" msgid="901206289846283445">"Send og motta meldinger uten mobil- eller wifi-nettverk"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Åpne Meldinger"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Slik fungerer det"</string>
+    <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
+    <skip />
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Venter …"</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Konfigurer opplåsingen med fingeravtrykk på nytt"</string>
     <string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"<xliff:g id="FINGERPRINT">%s</xliff:g> gjenkjennes ikke lenger."</string>
diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml
index 82052b5..2fea962 100644
--- a/core/res/res/values-ne/strings.xml
+++ b/core/res/res/values-ne/strings.xml
@@ -1943,6 +1943,8 @@
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"अफ छ"</string>
     <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
     <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> - <xliff:g id="END">%2$s</xliff:g>"</string>
+    <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
+    <skip />
     <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"कुनै पनि पात्रो"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> ले केही ध्वनिहरू म्युट गर्दै छ"</string>
     <string name="system_error_wipe_data" msgid="5910572292172208493">"तपाईंको यन्त्रसँग आन्तरिक समस्या छ, र तपाईंले फ्याक्ट्री डाटा रिसेट नगर्दासम्म यो अस्थिर रहन्छ।"</string>
@@ -2424,6 +2426,14 @@
     <string name="satellite_notification_manual_summary" msgid="901206289846283445">"मोबाइल वा Wi-Fi नेटवर्कविनै म्यासेजहरू पठाउनुहोस् र प्राप्त गर्नुहोस्"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Messages खोल्नुहोस्"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"यसले काम गर्ने तरिका"</string>
+    <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
+    <skip />
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"विचाराधीन..."</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"फिंगरप्रिन्ट अनलक फेरि सेटअप गर्नुहोस्"</string>
     <string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"<xliff:g id="FINGERPRINT">%s</xliff:g> अब पहिचान गर्न सकिँदैन।"</string>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index f591754..ac18bcf 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -1943,6 +1943,8 @@
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Uit"</string>
     <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
     <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> - <xliff:g id="END">%2$s</xliff:g>"</string>
+    <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
+    <skip />
     <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Elke agenda"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> zet sommige geluiden uit"</string>
     <string name="system_error_wipe_data" msgid="5910572292172208493">"Er is een intern probleem met je apparaat. Het apparaat kan instabiel zijn totdat u het apparaat terugzet naar de fabrieksinstellingen."</string>
@@ -2424,6 +2426,14 @@
     <string name="satellite_notification_manual_summary" msgid="901206289846283445">"Stuur en krijg berichten zonder mobiel of wifi-netwerk"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Berichten openen"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Hoe het werkt"</string>
+    <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
+    <skip />
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"In behandeling…"</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Ontgrendelen met vingerafdruk weer instellen"</string>
     <string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"<xliff:g id="FINGERPRINT">%s</xliff:g> wordt niet meer herkend."</string>
diff --git a/core/res/res/values-or/strings.xml b/core/res/res/values-or/strings.xml
index 1a952a3..c860657 100644
--- a/core/res/res/values-or/strings.xml
+++ b/core/res/res/values-or/strings.xml
@@ -329,7 +329,7 @@
     <string name="permgroupdesc_storage" msgid="5378659041354582769">"ଆପଣଙ୍କ ଡିଭାଇସରେ ଥିବା ଫାଇଲଗୁଡ଼ିକୁ ଆକ୍ସେସ କରନ୍ତୁ"</string>
     <string name="permgrouplab_readMediaAural" msgid="1858331312624942053">"ମ୍ୟୁଜିକ ଏବଂ ଅଡିଓ"</string>
     <string name="permgroupdesc_readMediaAural" msgid="7565467343667089595">"ଆପଣଙ୍କ ଡିଭାଇସରେ ମ୍ୟୁଜିକ ଏବଂ ଅଡିଓକୁ ଆକ୍ସେସ କରନ୍ତୁ"</string>
-    <string name="permgrouplab_readMediaVisual" msgid="4724874717811908660">"ଫଟୋ ଏବଂ ଭିଡିଓଗୁଡ଼ିକ"</string>
+    <string name="permgrouplab_readMediaVisual" msgid="4724874717811908660">"ଫଟୋ ଏବଂ ଭିଡିଓ"</string>
     <string name="permgroupdesc_readMediaVisual" msgid="4080463241903508688">"ଆପଣଙ୍କ ଡିଭାଇସରେ ଫଟୋ ଏବଂ ଭିଡିଓଗୁଡ଼ିକୁ ଆକ୍ସେସ କରନ୍ତୁ"</string>
     <string name="permgrouplab_microphone" msgid="2480597427667420076">"ମାଇକ୍ରୋଫୋନ"</string>
     <string name="permgroupdesc_microphone" msgid="1047786732792487722">"ଅଡିଓ ରେକର୍ଡ କରେ"</string>
@@ -1393,7 +1393,7 @@
     <string name="no_permissions" msgid="5729199278862516390">"କୌଣସି ଅନୁମତିର ଆବଶ୍ୟକତା ନାହିଁ"</string>
     <string name="perm_costs_money" msgid="749054595022779685">"ଶୁଳ୍କ ଲାଗୁ ହୋଇପାରେ"</string>
     <string name="dlg_ok" msgid="5103447663504839312">"ଠିକ ଅଛି"</string>
-    <string name="usb_charging_notification_title" msgid="1674124518282666955">"USB ମାଧ୍ୟମରେ ଏହି ଡିଭାଇସ୍‌ ଚାର୍ଜ ହେଉଛି"</string>
+    <string name="usb_charging_notification_title" msgid="1674124518282666955">"USB ମାଧ୍ୟମରେ ଏହି ଡିଭାଇସ ଚାର୍ଜ ହେଉଛି"</string>
     <string name="usb_supplying_notification_title" msgid="5378546632408101811">"USB ମାଧ୍ୟମରେ ଯୋଡ଼ାଯାଇଥିବା ଡିଭାଇସ୍ ଚାର୍ଜ ହେଉଛି"</string>
     <string name="usb_mtp_notification_title" msgid="1065989144124499810">"USB ଫାଇଲ୍ ଟ୍ରାନ୍ସଫର୍ ଚାଲୁ କରାଗଲା"</string>
     <string name="usb_ptp_notification_title" msgid="5043437571863443281">"USB ମାଧ୍ୟମରେ PTPକୁ ଚାଲୁ କରାଗଲା"</string>
@@ -1943,6 +1943,8 @@
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"ବନ୍ଦ ଅଛି"</string>
     <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
     <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> - <xliff:g id="END">%2$s</xliff:g>"</string>
+    <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
+    <skip />
     <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"ଯେକୌଣସି କ୍ୟାଲେଣ୍ଡର୍‌"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> କିଛି ସାଉଣ୍ଡକୁ ମ୍ୟୁଟ୍ କରୁଛି"</string>
     <string name="system_error_wipe_data" msgid="5910572292172208493">"ଆପଣଙ୍କ ଡିଭାଇସ୍‍ରେ ଏକ ସମସ୍ୟା ରହିଛି ଏବଂ ଆପଣ ଫ୍ୟାକ୍ଟୋରୀ ଡାଟା ରିସେଟ୍‍ ନକରିବା ପର୍ଯ୍ୟନ୍ତ ଏହା ଅସ୍ଥିର ରହିପାରେ।"</string>
@@ -2067,7 +2069,7 @@
     <string name="device_storage_monitor_notification_channel" msgid="5164244565844470758">"ଡିଭାଇସ୍‌ ଷ୍ଟୋରେଜ୍‌"</string>
     <string name="adb_debugging_notification_channel_tv" msgid="4764046459631031496">"USB ଡିବଗିଂ"</string>
     <string name="time_picker_hour_label" msgid="4208590187662336864">"ଘଣ୍ଟା"</string>
-    <string name="time_picker_minute_label" msgid="8307452311269824553">"ମିନିଟ୍"</string>
+    <string name="time_picker_minute_label" msgid="8307452311269824553">"ମିନିଟ"</string>
     <string name="time_picker_header_text" msgid="9073802285051516688">"ସମୟ ସେଟ୍‌ କରନ୍ତୁ"</string>
     <string name="time_picker_input_error" msgid="8386271930742451034">"ଏକ ମାନ୍ୟ ସମୟ ଲେଖନ୍ତୁ"</string>
     <string name="time_picker_prompt_label" msgid="303588544656363889">"ସମୟରେ ଟାଇପ୍‍ କରନ୍ତୁ"</string>
@@ -2424,6 +2426,14 @@
     <string name="satellite_notification_manual_summary" msgid="901206289846283445">"ଏକ ମୋବାଇଲ କିମ୍ବା ୱାଇ-ଫାଇ ନେଟୱାର୍କ ବିନା ମେସେଜ ପଠାନ୍ତୁ ଏବଂ ପାଆନ୍ତୁ"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Messages ଖୋଲନ୍ତୁ"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"ଏହା କିପରି କାମ କରେ"</string>
+    <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
+    <skip />
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"ବାକି ଅଛି…"</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"ଫିଙ୍ଗରପ୍ରିଣ୍ଟ ଅନଲକ ପୁଣି ସେଟ ଅପ କରନ୍ତୁ"</string>
     <string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"<xliff:g id="FINGERPRINT">%s</xliff:g>କୁ ଆଉ ଚିହ୍ନଟ କରାଯାଇପାରିବ ନାହିଁ।"</string>
@@ -2446,6 +2456,6 @@
     <string name="keyboard_shortcut_group_applications_calendar" msgid="3571770335653387606">"Calendar"</string>
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"କାଲକୁଲେଟର"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"Maps"</string>
-    <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"ଆପ୍ଲିକେସନଗୁଡ଼ିକ"</string>
+    <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"ଆପ୍ଲିକେସନ"</string>
     <string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"ଆପଣଙ୍କ ଟିପଚିହ୍ନକୁ ଆଉ ଚିହ୍ନଟ କରାଯାଇପାରିବ ନାହିଁ। ଫିଙ୍ଗରପ୍ରିଣ୍ଟ ଅନଲକ ପୁଣି ସେଟ ଅପ କରନ୍ତୁ।"</string>
 </resources>
diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml
index 6953509..63ebeb8 100644
--- a/core/res/res/values-pa/strings.xml
+++ b/core/res/res/values-pa/strings.xml
@@ -1943,6 +1943,8 @@
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"ਬੰਦ ਹੈ"</string>
     <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
     <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> - <xliff:g id="END">%2$s</xliff:g>"</string>
+    <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
+    <skip />
     <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"ਕੋਈ ਵੀ ਕੈਲੰਡਰ"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> ਕੁਝ ਧੁਨੀਆਂ ਨੂੰ ਮਿਊਟ ਕਰ ਰਹੀ ਹੈ"</string>
     <string name="system_error_wipe_data" msgid="5910572292172208493">"ਤੁਹਾਡੇ ਡੀਵਾਈਸ ਨਾਲ ਇੱਕ ਅੰਦਰੂਨੀ ਸਮੱਸਿਆ ਹੈ ਅਤੇ ਇਹ ਅਸਥਿਰ ਹੋ ਸਕਦੀ ਹੈ ਜਦੋਂ ਤੱਕ ਤੁਸੀਂ ਫੈਕਟਰੀ ਡਾਟਾ ਰੀਸੈੱਟ ਨਹੀਂ ਕਰਦੇ।"</string>
@@ -2424,6 +2426,14 @@
     <string name="satellite_notification_manual_summary" msgid="901206289846283445">"ਮੋਬਾਈਲ ਜਾਂ ਵਾਈ-ਫਾਈ ਨੈੱਟਵਰਕ ਤੋਂ ਬਿਨਾਂ ਸੁਨੇਹੇ ਭੇਜੋ ਅਤੇ ਪ੍ਰਾਪਤ ਕਰੋ"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Messages ਐਪ ਖੋਲ੍ਹੋ"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"ਇਹ ਕਿਵੇਂ ਕੰਮ ਕਰਦਾ ਹੈ"</string>
+    <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
+    <skip />
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"ਵਿਚਾਰ-ਅਧੀਨ..."</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"ਫਿੰਗਰਪ੍ਰਿੰਟ ਅਣਲਾਕ ਦਾ ਦੁਬਾਰਾ ਸੈੱਟਅੱਪ ਕਰੋ"</string>
     <string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"<xliff:g id="FINGERPRINT">%s</xliff:g> ਦੀ ਹੁਣ ਪਛਾਣ ਨਹੀਂ ਕੀਤੀ ਜਾ ਸਕਦੀ।"</string>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index 065e9ba..fa2b493 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -1945,6 +1945,8 @@
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Wyłączono"</string>
     <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
     <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> – <xliff:g id="END">%2$s</xliff:g>"</string>
+    <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
+    <skip />
     <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Dowolny kalendarz"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> wycisza niektóre dźwięki"</string>
     <string name="system_error_wipe_data" msgid="5910572292172208493">"W Twoim urządzeniu wystąpił problem wewnętrzny. Może być ono niestabilne, dopóki nie przywrócisz danych fabrycznych."</string>
@@ -2426,6 +2428,14 @@
     <string name="satellite_notification_manual_summary" msgid="901206289846283445">"Wysyłaj i odbieraj wiadomości bez sieci komórkowej czy Wi-Fi"</string>
     <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>
+    <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
+    <skip />
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Oczekiwanie…"</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Skonfiguruj ponownie odblokowywanie odciskiem palca"</string>
     <string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"Ten odcisk palca (<xliff:g id="FINGERPRINT">%s</xliff:g>) nie jest już rozpoznawany."</string>
diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml
index c972e7a..76a8dc1 100644
--- a/core/res/res/values-pt-rBR/strings.xml
+++ b/core/res/res/values-pt-rBR/strings.xml
@@ -1394,7 +1394,7 @@
     <string name="no_permissions" msgid="5729199278862516390">"Nenhuma permissão necessária"</string>
     <string name="perm_costs_money" msgid="749054595022779685">"isso pode lhe custar dinheiro"</string>
     <string name="dlg_ok" msgid="5103447663504839312">"OK"</string>
-    <string name="usb_charging_notification_title" msgid="1674124518282666955">"Carregar dispositivo via USB"</string>
+    <string name="usb_charging_notification_title" msgid="1674124518282666955">"Dispositivo carregando via USB"</string>
     <string name="usb_supplying_notification_title" msgid="5378546632408101811">"Carregando dispositivo conectado via USB"</string>
     <string name="usb_mtp_notification_title" msgid="1065989144124499810">"Transferência de arquivo via USB ativada"</string>
     <string name="usb_ptp_notification_title" msgid="5043437571863443281">"PTP via USB ativado"</string>
@@ -1402,7 +1402,7 @@
     <string name="usb_midi_notification_title" msgid="7404506788950595557">"MIDI via USB ativado"</string>
     <string name="usb_uvc_notification_title" msgid="2030032862673400008">"Dispositivo conectado como Webcam"</string>
     <string name="usb_accessory_notification_title" msgid="1385394660861956980">"Acessório USB conectado"</string>
-    <string name="usb_notification_message" msgid="4715163067192110676">"Toque para mais opções."</string>
+    <string name="usb_notification_message" msgid="4715163067192110676">"Toque para mais opções"</string>
     <string name="usb_power_notification_message" msgid="7284765627437897702">"Carregando dispositivo conectado. Toque para ver mais opções."</string>
     <string name="usb_unsupported_audio_accessory_title" msgid="2335775548086533065">"Acessório de áudio analógico detectado"</string>
     <string name="usb_unsupported_audio_accessory_message" msgid="1300168007129796621">"O dispositivo anexo não é compatível com esse smartphone. Toque para saber mais."</string>
@@ -1944,6 +1944,8 @@
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Desativada"</string>
     <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
     <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> a <xliff:g id="END">%2$s</xliff:g>"</string>
+    <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
+    <skip />
     <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Qualquer agenda"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> está silenciando alguns sons"</string>
     <string name="system_error_wipe_data" msgid="5910572292172208493">"Há um problema interno com seu dispositivo. Ele pode ficar instável até que você faça a redefinição para configuração original."</string>
@@ -2425,6 +2427,14 @@
     <string name="satellite_notification_manual_summary" msgid="901206289846283445">"Enviar e receber mensagens sem uma rede móvel ou Wi-Fi"</string>
     <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>
+    <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
+    <skip />
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Pendente…"</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Configurar o Desbloqueio por impressão digital de novo"</string>
     <string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"A impressão digital <xliff:g id="FINGERPRINT">%s</xliff:g> não é mais reconhecida."</string>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index 8dddd95..388c5a9 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -1944,6 +1944,8 @@
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Desativada"</string>
     <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
     <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> – <xliff:g id="END">%2$s</xliff:g>"</string>
+    <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
+    <skip />
     <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Qualquer calendário"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> está a desativar alguns sons."</string>
     <string name="system_error_wipe_data" msgid="5910572292172208493">"Existe um problema interno no seu dispositivo e pode ficar instável até efetuar uma reposição de dados de fábrica."</string>
@@ -2425,6 +2427,14 @@
     <string name="satellite_notification_manual_summary" msgid="901206289846283445">"Envie e receba mensagens sem uma rede móvel ou Wi-Fi"</string>
     <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>
+    <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
+    <skip />
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Pendente…"</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Configure o Desbloqueio por impressão digital novamente"</string>
     <string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"Já não é possível reconhecer <xliff:g id="FINGERPRINT">%s</xliff:g>."</string>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index c972e7a..76a8dc1 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -1394,7 +1394,7 @@
     <string name="no_permissions" msgid="5729199278862516390">"Nenhuma permissão necessária"</string>
     <string name="perm_costs_money" msgid="749054595022779685">"isso pode lhe custar dinheiro"</string>
     <string name="dlg_ok" msgid="5103447663504839312">"OK"</string>
-    <string name="usb_charging_notification_title" msgid="1674124518282666955">"Carregar dispositivo via USB"</string>
+    <string name="usb_charging_notification_title" msgid="1674124518282666955">"Dispositivo carregando via USB"</string>
     <string name="usb_supplying_notification_title" msgid="5378546632408101811">"Carregando dispositivo conectado via USB"</string>
     <string name="usb_mtp_notification_title" msgid="1065989144124499810">"Transferência de arquivo via USB ativada"</string>
     <string name="usb_ptp_notification_title" msgid="5043437571863443281">"PTP via USB ativado"</string>
@@ -1402,7 +1402,7 @@
     <string name="usb_midi_notification_title" msgid="7404506788950595557">"MIDI via USB ativado"</string>
     <string name="usb_uvc_notification_title" msgid="2030032862673400008">"Dispositivo conectado como Webcam"</string>
     <string name="usb_accessory_notification_title" msgid="1385394660861956980">"Acessório USB conectado"</string>
-    <string name="usb_notification_message" msgid="4715163067192110676">"Toque para mais opções."</string>
+    <string name="usb_notification_message" msgid="4715163067192110676">"Toque para mais opções"</string>
     <string name="usb_power_notification_message" msgid="7284765627437897702">"Carregando dispositivo conectado. Toque para ver mais opções."</string>
     <string name="usb_unsupported_audio_accessory_title" msgid="2335775548086533065">"Acessório de áudio analógico detectado"</string>
     <string name="usb_unsupported_audio_accessory_message" msgid="1300168007129796621">"O dispositivo anexo não é compatível com esse smartphone. Toque para saber mais."</string>
@@ -1944,6 +1944,8 @@
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Desativada"</string>
     <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
     <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> a <xliff:g id="END">%2$s</xliff:g>"</string>
+    <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
+    <skip />
     <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Qualquer agenda"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> está silenciando alguns sons"</string>
     <string name="system_error_wipe_data" msgid="5910572292172208493">"Há um problema interno com seu dispositivo. Ele pode ficar instável até que você faça a redefinição para configuração original."</string>
@@ -2425,6 +2427,14 @@
     <string name="satellite_notification_manual_summary" msgid="901206289846283445">"Enviar e receber mensagens sem uma rede móvel ou Wi-Fi"</string>
     <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>
+    <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
+    <skip />
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Pendente…"</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Configurar o Desbloqueio por impressão digital de novo"</string>
     <string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"A impressão digital <xliff:g id="FINGERPRINT">%s</xliff:g> não é mais reconhecida."</string>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index 014a115..384d1b3 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -1944,6 +1944,8 @@
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Dezactivată"</string>
     <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
     <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> – <xliff:g id="END">%2$s</xliff:g>"</string>
+    <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
+    <skip />
     <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Orice calendar"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> dezactivează anumite sunete"</string>
     <string name="system_error_wipe_data" msgid="5910572292172208493">"A apărut o problemă internă pe dispozitiv, iar acesta poate fi instabil până la revenirea la setările din fabrică."</string>
@@ -2425,6 +2427,14 @@
     <string name="satellite_notification_manual_summary" msgid="901206289846283445">"Trimite și primește mesaje fără o rețea mobilă sau Wi-Fi"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Deschide Mesaje"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Cum funcționează"</string>
+    <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
+    <skip />
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"În așteptare..."</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Configurează din nou Deblocarea cu amprenta"</string>
     <string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"<xliff:g id="FINGERPRINT">%s</xliff:g> nu mai poate fi recunoscută."</string>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index 1e819d6..f7e3610 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -1945,6 +1945,8 @@
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Отключено"</string>
     <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
     <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> – <xliff:g id="END">%2$s</xliff:g>"</string>
+    <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
+    <skip />
     <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Любой календарь"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> приглушает некоторые звуки."</string>
     <string name="system_error_wipe_data" msgid="5910572292172208493">"Произошла внутренняя ошибка, и устройство может работать нестабильно, пока вы не выполните сброс настроек."</string>
@@ -2426,6 +2428,14 @@
     <string name="satellite_notification_manual_summary" msgid="901206289846283445">"Отправляйте и получайте сообщения без подключения к мобильной сети или Wi-Fi."</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Открыть Сообщения"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Узнать принцип работы"</string>
+    <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
+    <skip />
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Обработка…"</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Настройте разблокировку по отпечатку пальца заново"</string>
     <string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"Отпечаток \"<xliff:g id="FINGERPRINT">%s</xliff:g>\" больше нельзя распознать."</string>
diff --git a/core/res/res/values-si/strings.xml b/core/res/res/values-si/strings.xml
index 8c80803..ea404f1 100644
--- a/core/res/res/values-si/strings.xml
+++ b/core/res/res/values-si/strings.xml
@@ -1943,6 +1943,8 @@
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"ක්‍රියාවිරහිතයි"</string>
     <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
     <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> - <xliff:g id="END">%2$s</xliff:g>"</string>
+    <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
+    <skip />
     <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"ඕනෑම දින දර්ශනයක්"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> සමහර ශබ්ද නිහඬ කරමින්"</string>
     <string name="system_error_wipe_data" msgid="5910572292172208493">"ඔබේ උපාංගය සමගින් ගැටලුවක් ඇති අතර, ඔබේ කර්මාන්තශාලා දත්ත යළි සකසන තෙක් එය අස්ථායි විය හැකිය."</string>
@@ -2424,6 +2426,14 @@
     <string name="satellite_notification_manual_summary" msgid="901206289846283445">"ජංගම හෝ Wi-Fi ජාලයකින් තොරව පණිවිඩ යැවීම සහ ලැබීම"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Messages විවෘත කරන්න"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"එය ක්‍රියා කරන ආකාරය"</string>
+    <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
+    <skip />
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"පොරොත්තුයි..."</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"ඇඟිලි සලකුණු අගුලු හැරීම නැවත සකසන්න"</string>
     <string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"<xliff:g id="FINGERPRINT">%s</xliff:g> තවදුරටත් හඳුනා ගත නොහැක."</string>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index cb3cd61..eeb3081 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -1945,6 +1945,8 @@
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Vypnuté"</string>
     <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
     <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> – <xliff:g id="END">%2$s</xliff:g>"</string>
+    <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
+    <skip />
     <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Ľubovoľný kalendár"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> vypína niektoré zvuky"</string>
     <string name="system_error_wipe_data" msgid="5910572292172208493">"Vo vašom zariadení došlo k internému problému. Môže byť nestabilné, kým neobnovíte jeho výrobné nastavenia."</string>
@@ -2405,13 +2407,13 @@
     <string name="keyboard_layout_notification_more_than_three_selected_message" msgid="1581834181578206937">"Rozloženie klávesnice je nastavené na jazyky <xliff:g id="LAYOUT_1">%1$s</xliff:g>, <xliff:g id="LAYOUT_2">%2$s</xliff:g> a <xliff:g id="LAYOUT_3">%3$s</xliff:g>… Môžete to zmeniť klepnutím."</string>
     <string name="keyboard_layout_notification_multiple_selected_title" msgid="5242444914367024499">"Fyzické klávesnice sú nakonfigurované"</string>
     <string name="keyboard_layout_notification_multiple_selected_message" msgid="6576533454124419202">"Klávesnice si zobrazíte klepnutím"</string>
-    <string name="profile_label_private" msgid="6463418670715290696">"Súkromný"</string>
+    <string name="profile_label_private" msgid="6463418670715290696">"Súkromné"</string>
     <string name="profile_label_clone" msgid="769106052210954285">"Klon"</string>
-    <string name="profile_label_work" msgid="3495359133038584618">"Pracovný"</string>
+    <string name="profile_label_work" msgid="3495359133038584618">"Pracovné"</string>
     <string name="profile_label_work_2" msgid="4691533661598632135">"2. pracovný"</string>
     <string name="profile_label_work_3" msgid="4834572253956798917">"3. pracovný"</string>
-    <string name="profile_label_test" msgid="9168641926186071947">"Testovací"</string>
-    <string name="profile_label_communal" msgid="8743921499944800427">"Spoločný"</string>
+    <string name="profile_label_test" msgid="9168641926186071947">"Test"</string>
+    <string name="profile_label_communal" msgid="8743921499944800427">"Spoločné"</string>
     <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Pracovný profil"</string>
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Súkromný priestor"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klon"</string>
@@ -2426,6 +2428,14 @@
     <string name="satellite_notification_manual_summary" msgid="901206289846283445">"Odosielajte a prijímajte správy bez mobilnej siete či siete Wi‑Fi"</string>
     <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>
+    <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
+    <skip />
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Nespracovaná…"</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Znova nastavte odomknutie odtlačkom prsta"</string>
     <string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"<xliff:g id="FINGERPRINT">%s</xliff:g> sa už nedari rozpoznať."</string>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index 5378875..514ad16 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -1945,6 +1945,8 @@
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Izklopljeno"</string>
     <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
     <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g>–<xliff:g id="END">%2$s</xliff:g>"</string>
+    <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
+    <skip />
     <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Kateri koli koledar"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> izklaplja nekatere zvoke"</string>
     <string name="system_error_wipe_data" msgid="5910572292172208493">"Vaša naprava ima notranjo napako in bo morda nestabilna, dokler je ne ponastavite na tovarniške nastavitve."</string>
@@ -2426,6 +2428,14 @@
     <string name="satellite_notification_manual_summary" msgid="901206289846283445">"Pošiljanje in prejemanje sporočil brez mobilnega omrežja ali omrežja Wi-Fi"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Odpri Sporočila"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Kako deluje"</string>
+    <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
+    <skip />
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"V teku …"</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Vnovična nastavitev odklepanja s prstnim odtisom"</string>
     <string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"Odtisa »<xliff:g id="FINGERPRINT">%s</xliff:g>« ni več mogoče prepoznati."</string>
diff --git a/core/res/res/values-sq/strings.xml b/core/res/res/values-sq/strings.xml
index 4c8dfbc..8d535b9 100644
--- a/core/res/res/values-sq/strings.xml
+++ b/core/res/res/values-sq/strings.xml
@@ -1943,6 +1943,8 @@
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Çaktivizuar"</string>
     <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
     <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> - <xliff:g id="END">%2$s</xliff:g>"</string>
+    <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
+    <skip />
     <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Çdo kalendar"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> po çaktivizon disa tinguj"</string>
     <string name="system_error_wipe_data" msgid="5910572292172208493">"Ka një problem të brendshëm me pajisjen tënde. Ajo mund të jetë e paqëndrueshme derisa të rivendosësh të dhënat në gjendje fabrike."</string>
@@ -2424,6 +2426,14 @@
     <string name="satellite_notification_manual_summary" msgid="901206289846283445">"Dërgo dhe merr mesazhe pa një rrjet celular ose Wi-Fi"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Hap \"Mesazhet\""</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Si funksionon"</string>
+    <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
+    <skip />
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Në pritje..."</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Konfiguro përsëri \"Shkyçjen me gjurmën e gishtit\""</string>
     <string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"<xliff:g id="FINGERPRINT">%s</xliff:g> nuk mund të njihet më."</string>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index c9f44db..af75a5c 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -1944,6 +1944,8 @@
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Искључено"</string>
     <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
     <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g>–<xliff:g id="END">%2$s</xliff:g>"</string>
+    <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
+    <skip />
     <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Било који календар"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> искључује неке звуке"</string>
     <string name="system_error_wipe_data" msgid="5910572292172208493">"Дошло је до интерног проблема у вези са уређајем и можда ће бити нестабилан док не обавите ресетовање на фабричка подешавања."</string>
@@ -2425,6 +2427,14 @@
     <string name="satellite_notification_manual_summary" msgid="901206289846283445">"Шаљите и примајте поруке без мобилне или WiFi мреже"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Отвори Messages"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Принцип рада"</string>
+    <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
+    <skip />
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"На чекању..."</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Поново подесите откључавање отиском прста"</string>
     <string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"<xliff:g id="FINGERPRINT">%s</xliff:g> више не може да се препозна."</string>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index 574b4e8..0cff2e5 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -201,7 +201,7 @@
     <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"Jobbprofilen är inte längre tillgänglig på enheten"</string>
     <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"För många försök med lösenord"</string>
     <string name="device_ownership_relinquished" msgid="4080886992183195724">"Administratören tillåter inte längre privat bruk av enheten"</string>
-    <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"Privat område har tagits bort"</string>
+    <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"Privat utrymme har tagits bort"</string>
     <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"Din organisation tillåter inte privata utrymmen på den här hanterade enheten."</string>
     <string name="network_logging_notification_title" msgid="554983187553845004">"Enheten hanteras"</string>
     <string name="network_logging_notification_text" msgid="1327373071132562512">"Organisationen hanterar den här enheten och kan övervaka nätverkstrafiken. Tryck om du vill veta mer."</string>
@@ -1943,6 +1943,8 @@
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Av"</string>
     <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
     <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g>–<xliff:g id="END">%2$s</xliff:g>"</string>
+    <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
+    <skip />
     <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Alla kalendrar"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> stänger av vissa ljud"</string>
     <string name="system_error_wipe_data" msgid="5910572292172208493">"Ett internt problem har uppstått i enheten, och det kan hända att problemet kvarstår tills du återställer standardinställningarna."</string>
@@ -2411,10 +2413,10 @@
     <string name="profile_label_test" msgid="9168641926186071947">"Test"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"Allmän"</string>
     <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Jobbprofil"</string>
-    <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Privat område"</string>
+    <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Privat utrymme"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klona"</string>
     <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Allmän"</string>
-    <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Privat område"</string>
+    <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Privat utrymme"</string>
     <string name="redacted_notification_message" msgid="1520587845842228816">"Känsligt aviseringsinnehåll dolt"</string>
     <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Av säkerhetsskäl döljs appinnehållet vid skärmdelning"</string>
@@ -2424,6 +2426,14 @@
     <string name="satellite_notification_manual_summary" msgid="901206289846283445">"Skicka och ta emot meddelanden utan ett mobil- eller wifi-nätverk"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Öppna Messages"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Så fungerar det"</string>
+    <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
+    <skip />
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Väntar …"</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Konfigurera fingeravtryckslås igen"</string>
     <string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"Det går inte längre att känna igen <xliff:g id="FINGERPRINT">%s</xliff:g>."</string>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index 676e7ab..bf51892 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -1943,6 +1943,8 @@
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Imezimwa"</string>
     <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
     <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> - <xliff:g id="END">%2$s</xliff:g>"</string>
+    <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
+    <skip />
     <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Kalenda yoyote"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> inazima baadhi ya sauti"</string>
     <string name="system_error_wipe_data" msgid="5910572292172208493">"Kuna hitilafu ya ndani ya kifaa chako, na huenda kisiwe thabiti mpaka urejeshe mipangilio ya kiwandani."</string>
@@ -2424,6 +2426,14 @@
     <string name="satellite_notification_manual_summary" msgid="901206289846283445">"Tuma na upokee ujumbe bila kutumia mtandao wa simu wala Wi-Fi"</string>
     <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>
+    <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
+    <skip />
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Inashughulikiwa..."</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Weka tena mipangilio ya Kufungua kwa Alama ya Kidole"</string>
     <string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"<xliff:g id="FINGERPRINT">%s</xliff:g> haitambuliki tena."</string>
diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml
index 35cad78..8ff19a9 100644
--- a/core/res/res/values-ta/strings.xml
+++ b/core/res/res/values-ta/strings.xml
@@ -1943,6 +1943,8 @@
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"ஆஃப்"</string>
     <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
     <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> - <xliff:g id="END">%2$s</xliff:g>"</string>
+    <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
+    <skip />
     <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"ஏதேனும் கேலெண்டர்"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> சில ஒலிகளை முடக்குகிறது"</string>
     <string name="system_error_wipe_data" msgid="5910572292172208493">"சாதனத்தில் அகச் சிக்கல் இருக்கிறது, அதனை ஆரம்பநிலைக்கு மீட்டமைக்கும் வரை நிலையற்று இயங்கலாம்."</string>
@@ -2424,6 +2426,14 @@
     <string name="satellite_notification_manual_summary" msgid="901206289846283445">"மொபைல்/வைஃபை நெட்வொர்க் இல்லாமல் மெசேஜ்களை அனுப்பலாம், பெறலாம்"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Messages ஆப்ஸைத் திறக்கவும்"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"இது செயல்படும் விதம்"</string>
+    <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
+    <skip />
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"நிலுவையிலுள்ளது..."</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"கைரேகை அன்லாக் அம்சத்தை மீண்டும் அமையுங்கள்"</string>
     <string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"<xliff:g id="FINGERPRINT">%s</xliff:g>ஐ இனி அடையாளம் காண முடியாது."</string>
diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml
index fd78f1d..492dca7 100644
--- a/core/res/res/values-te/strings.xml
+++ b/core/res/res/values-te/strings.xml
@@ -1943,6 +1943,8 @@
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"ఆఫ్‌లో ఉంది"</string>
     <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
     <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> - <xliff:g id="END">%2$s</xliff:g>"</string>
+    <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
+    <skip />
     <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"ఏదైనా క్యాలెండర్"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> కొన్ని ధ్వనులను మ్యూట్ చేస్తోంది"</string>
     <string name="system_error_wipe_data" msgid="5910572292172208493">"మీ పరికరంతో అంతర్గత సమస్య ఏర్పడింది మరియు మీరు ఫ్యాక్టరీ డేటా రీసెట్ చేసే వరకు అస్థిరంగా ఉంటుంది."</string>
@@ -2424,6 +2426,14 @@
     <string name="satellite_notification_manual_summary" msgid="901206289846283445">"మొబైల్ లేదా Wi-Fi నెట్‌వర్క్ లేకుండా మెసేజ్‌లను పంపండి, స్వీకరించండి"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Messagesను తెరవండి"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"ఇది ఎలా పని చేస్తుంది"</string>
+    <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
+    <skip />
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"పెండింగ్‌లో ఉంది..."</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"వేలిముద్ర అన్‌లాక్‌ను మళ్లీ సెటప్ చేయండి"</string>
     <string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"<xliff:g id="FINGERPRINT">%s</xliff:g>‌ను ఇకపై గుర్తించడం సాధ్యం కాదు."</string>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index cbac93d..90a90ee 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -1943,6 +1943,8 @@
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"ปิด"</string>
     <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
     <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> - <xliff:g id="END">%2$s</xliff:g>"</string>
+    <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
+    <skip />
     <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"ปฏิทินทั้งหมด"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> กำลังปิดเสียงบางรายการ"</string>
     <string name="system_error_wipe_data" msgid="5910572292172208493">"อุปกรณ์ของคุณเกิดปัญหาภายในเครื่อง อุปกรณ์อาจทำงานไม่เสถียรจนกว่าคุณจะรีเซ็ตข้อมูลเป็นค่าเริ่มต้น"</string>
@@ -2424,6 +2426,14 @@
     <string name="satellite_notification_manual_summary" msgid="901206289846283445">"รับและส่งข้อความโดยไม่ต้องใช้เครือข่ายมือถือหรือ Wi-Fi"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"เปิด Messages"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"วิธีการทำงาน"</string>
+    <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
+    <skip />
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"รอดำเนินการ..."</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"ตั้งค่าการปลดล็อกด้วยลายนิ้วมืออีกครั้ง"</string>
     <string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"ระบบไม่จดจำ <xliff:g id="FINGERPRINT">%s</xliff:g> อีกต่อไป"</string>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index f9edcdc..f4df0e2 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -1943,6 +1943,8 @@
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Naka-off"</string>
     <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
     <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g>, <xliff:g id="END">%2$s</xliff:g>"</string>
+    <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
+    <skip />
     <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Anumang kalendaryo"</string>
     <string name="muted_by" msgid="91464083490094950">"Minu-mute ng <xliff:g id="THIRD_PARTY">%1$s</xliff:g> ang ilang tunog"</string>
     <string name="system_error_wipe_data" msgid="5910572292172208493">"May internal na problema sa iyong device, at maaaring hindi ito maging stable hanggang sa i-reset mo ang factory data."</string>
@@ -2424,6 +2426,14 @@
     <string name="satellite_notification_manual_summary" msgid="901206289846283445">"Magpadala at tumanggap ng mga mensahe nang walang mobile o Wi-Fi network"</string>
     <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>
+    <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
+    <skip />
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Nakabinbin..."</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"I-set up ulit ang Pag-unlock Gamit ang Fingerprint"</string>
     <string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"Hindi na makilala ang <xliff:g id="FINGERPRINT">%s</xliff:g>."</string>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index fea0951..77c3ee2 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -1943,6 +1943,8 @@
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Kapalı"</string>
     <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
     <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g>-<xliff:g id="END">%2$s</xliff:g>"</string>
+    <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
+    <skip />
     <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Tüm takvimler"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> bazı sesleri kapatıyor"</string>
     <string name="system_error_wipe_data" msgid="5910572292172208493">"Cihazınızla ilgili dahili bir sorun oluştu ve fabrika verilerine sıfırlama işlemi gerçekleştirilene kadar kararsız çalışabilir."</string>
@@ -2424,6 +2426,14 @@
     <string name="satellite_notification_manual_summary" msgid="901206289846283445">"Mobil veya kablosuz ağ kullanmadan mesaj gönderip alın"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Mesajlar\'ı aç"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"İşleyiş şekli"</string>
+    <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
+    <skip />
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Bekliyor..."</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Parmak İzi Kilidi\'ni tekrar kurun"</string>
     <string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"<xliff:g id="FINGERPRINT">%s</xliff:g> artık tanınamayacak."</string>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index 68f85b8..2f1a1d5 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -1945,6 +1945,8 @@
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Вимкнено"</string>
     <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
     <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> – <xliff:g id="END">%2$s</xliff:g>"</string>
+    <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
+    <skip />
     <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"З усіх календарів"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> вимикає деякі звуки"</string>
     <string name="system_error_wipe_data" msgid="5910572292172208493">"Через внутрішню помилку ваш пристрій може працювати нестабільно. Відновіть заводські налаштування."</string>
@@ -2426,6 +2428,14 @@
     <string name="satellite_notification_manual_summary" msgid="901206289846283445">"Надсилайте й отримуйте текстові повідомлення без мобільної мережі або Wi-Fi"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Відкрийте Повідомлення"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Як це працює"</string>
+    <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
+    <skip />
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Обробка…"</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Налаштуйте розблокування відбитком пальця повторно"</string>
     <string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"Відбиток пальця <xliff:g id="FINGERPRINT">%s</xliff:g> більше не розпізнається."</string>
diff --git a/core/res/res/values-ur/strings.xml b/core/res/res/values-ur/strings.xml
index 4f09717..6c78283 100644
--- a/core/res/res/values-ur/strings.xml
+++ b/core/res/res/values-ur/strings.xml
@@ -1943,6 +1943,8 @@
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"آف ہے"</string>
     <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">"، "</string>
     <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> - <xliff:g id="END">%2$s</xliff:g>"</string>
+    <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
+    <skip />
     <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"کوئی بھی کیلنڈر"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> کچھ آوازوں کو خاموش کر رہا ہے"</string>
     <string name="system_error_wipe_data" msgid="5910572292172208493">"آپ کے آلہ میں ایک داخلی مسئلہ ہے اور جب تک آپ فیکٹری ڈیٹا کو دوبارہ ترتیب نہیں دے دیتے ہیں، ہوسکتا ہے کہ یہ غیر مستحکم رہے۔"</string>
@@ -2424,6 +2426,14 @@
     <string name="satellite_notification_manual_summary" msgid="901206289846283445">"‏موبائل یا Wi-Fi نیٹ ورک کے بغیر پیغامات بھیجیں اور موصول کریں"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"پیغامات ایپ کو کھولیں"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"اس کے کام کرنے کا طریقہ"</string>
+    <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
+    <skip />
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"زیر التواء..."</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"فنگر پرنٹ اَن لاک کو دوبارہ سیٹ اپ کریں"</string>
     <string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"<xliff:g id="FINGERPRINT">%s</xliff:g> مزید پہچانا نہیں جا سکتا۔"</string>
diff --git a/core/res/res/values-uz/strings.xml b/core/res/res/values-uz/strings.xml
index 1bb3bcdc..ac70b93 100644
--- a/core/res/res/values-uz/strings.xml
+++ b/core/res/res/values-uz/strings.xml
@@ -1943,6 +1943,8 @@
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Oʻchiq"</string>
     <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
     <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> – <xliff:g id="END">%2$s</xliff:g>"</string>
+    <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
+    <skip />
     <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Har qanday taqvim"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> ayrim tovushlarni ovozsiz qilgan"</string>
     <string name="system_error_wipe_data" msgid="5910572292172208493">"Qurilmangiz bilan bog‘liq ichki muammo mavjud. U zavod sozlamalari tiklanmaguncha barqaror ishlamasligi mumkin."</string>
@@ -2424,6 +2426,14 @@
     <string name="satellite_notification_manual_summary" msgid="901206289846283445">"Mobil yoki Wi-Fi tarmoq blan aloqa yoʻqligida xabar yuboring va qabul qiling"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Xabarlar ilovasini ochish"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Ishlash tartibi"</string>
+    <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
+    <skip />
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Kutilmoqda..."</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Barmoq izi bilan ochish funksiyasini qayta sozlang"</string>
     <string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"<xliff:g id="FINGERPRINT">%s</xliff:g> endi tanilmaydi."</string>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index 3b1b21d..d4551b1 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -335,7 +335,7 @@
     <string name="permgroupdesc_microphone" msgid="1047786732792487722">"ghi âm"</string>
     <string name="permgrouplab_activityRecognition" msgid="3324466667921775766">"Hoạt động thể chất"</string>
     <string name="permgroupdesc_activityRecognition" msgid="4725624819457670704">"truy cập vào hoạt động thể chất"</string>
-    <string name="permgrouplab_camera" msgid="9090413408963547706">"Máy ảnh"</string>
+    <string name="permgrouplab_camera" msgid="9090413408963547706">"Camera"</string>
     <string name="permgroupdesc_camera" msgid="7585150538459320326">"chụp ảnh và quay video"</string>
     <string name="permgrouplab_nearby_devices" msgid="5529147543651181991">"Thiết bị ở gần"</string>
     <string name="permgroupdesc_nearby_devices" msgid="3213561597116913508">"khám phá và kết nối với các thiết bị ở gần"</string>
@@ -1943,6 +1943,8 @@
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Tắt"</string>
     <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
     <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> – <xliff:g id="END">%2$s</xliff:g>"</string>
+    <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
+    <skip />
     <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Bất kỳ lịch nào"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> đang tắt một số âm thanh"</string>
     <string name="system_error_wipe_data" msgid="5910572292172208493">"Đã xảy ra sự cố nội bộ với thiết bị của bạn và thiết bị có thể sẽ không ổn định cho tới khi bạn thiết lập lại dữ liệu ban đầu."</string>
@@ -2135,7 +2137,7 @@
     <string name="review_notification_settings_dismiss" msgid="4160916504616428294">"Đóng"</string>
     <string name="notification_app_name_system" msgid="3045196791746735601">"Hệ thống"</string>
     <string name="notification_app_name_settings" msgid="9088548800899952531">"Cài đặt"</string>
-    <string name="notification_appops_camera_active" msgid="8177643089272352083">"Máy ảnh"</string>
+    <string name="notification_appops_camera_active" msgid="8177643089272352083">"Camera"</string>
     <string name="notification_appops_microphone_active" msgid="581333393214739332">"Micrô"</string>
     <string name="notification_appops_overlay_active" msgid="5571732753262836481">"hiển thị qua các ứng dụng khác trên màn hình của bạn"</string>
     <string name="notification_feedback_indicator" msgid="663476517711323016">"Gửi ý kiến phản hồi"</string>
@@ -2424,6 +2426,14 @@
     <string name="satellite_notification_manual_summary" msgid="901206289846283445">"Gửi và nhận tin nhắn mà không cần mạng di động hoặc Wi-Fi"</string>
     <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>
+    <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
+    <skip />
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Đang chờ xử lý..."</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Thiết lập lại tính năng Mở khoá bằng vân tay"</string>
     <string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"Không nhận dạng được <xliff:g id="FINGERPRINT">%s</xliff:g> nữa."</string>
diff --git a/core/res/res/values-watch-v36/config.xml b/core/res/res/values-watch-v36/config.xml
index c8f347af..bb9da17 100644
--- a/core/res/res/values-watch-v36/config.xml
+++ b/core/res/res/values-watch-v36/config.xml
@@ -17,4 +17,5 @@
 <resources>
     <!-- Overrides system value -->
     <dimen name="config_buttonCornerRadius">26dp</dimen>
+    <dimen name="config_bottomDialogCornerRadius">18dp</dimen>
 </resources>
diff --git a/core/res/res/values-watch-v36/dimens_material.xml b/core/res/res/values-watch-v36/dimens_material.xml
index ad3c1a3..ffa3b9c 100644
--- a/core/res/res/values-watch-v36/dimens_material.xml
+++ b/core/res/res/values-watch-v36/dimens_material.xml
@@ -22,6 +22,12 @@
     <dimen name="btn_lineHeight">18sp</dimen>
     <dimen name="btn_textSize">15sp</dimen>
 
+    <!-- values for material3 AlertDialog -->
+    <dimen name="dialog_btn_negative_width">60dp</dimen>
+    <dimen name="dialog_btn_negative_height">60dp</dimen>
+    <dimen name="dialog_btn_confirm_width">62dp</dimen>
+    <dimen name="dialog_btn_confirm_height">60dp</dimen>
+
     <!-- Opacity factor for disabled material3 widget -->
     <dimen name="disabled_alpha_device_default">0.12</dimen>
     <dimen name="primary_content_alpha_device_default">0.38</dimen>
diff --git a/core/res/res/values-watch-v36/styles_material.xml b/core/res/res/values-watch-v36/styles_material.xml
index 32a22bb..b2760e7 100644
--- a/core/res/res/values-watch-v36/styles_material.xml
+++ b/core/res/res/values-watch-v36/styles_material.xml
@@ -44,7 +44,7 @@
     <!--  Text Styles  -->
     <!-- TextAppearance for Material Button - Filled  -->
     <style name="TextAppearance.Widget.Button.Material.Filled" parent="TextAppearance.Widget.Button.Material">
-        <item name="textColor">@color/btn_material_filled_text_color</item>
+        <item name="textColor">@color/btn_material_filled_content_color</item>
     </style>
 
     <!-- TextAppearance for Material Button - Filled Tonal  -->
@@ -52,7 +52,31 @@
         <item name="android:fontFamily">font-family-flex-device-default</item>
         <item name="android:fontVariationSettings">"'wdth' 90, 'wght' 500, 'ROND' 100, 'opsz' 15, 'GRAD' 0"</item>
         <item name="textSize">@dimen/btn_textSize</item>
-        <item name="textColor">@color/btn_material_filled_tonal_text_color</item>
+        <item name="textColor">@color/btn_material_filled_tonal_content_color</item>
         <item name="lineHeight">@dimen/btn_lineHeight</item>
     </style>
+
+    <!--  AlertDialog Styles  -->
+    <style name="Widget.DeviceDefault.Button.ButtonBar.AlertDialog" parent="Widget.DeviceDefault.Button">
+        <item name="android:textSize">0sp</item>
+        <item name="android:gravity">center</item>
+        <item name="android:paddingStart">0dp</item>
+        <item name="android:paddingEnd">0dp</item>
+        <item name="android:drawablePadding">0dp</item>
+    </style>
+
+    <style name="Widget.DeviceDefault.Button.ButtonBar.AlertDialog.Confirm" parent="Widget.DeviceDefault.Button.ButtonBar.AlertDialog">
+        <!-- Use a ImageView as background -->
+        <item name="background">@android:color/transparent</item>
+        <item name="minWidth">@dimen/dialog_btn_confirm_width</item>
+        <item name="minHeight">@dimen/dialog_btn_confirm_height</item>
+    </style>
+
+    <style name="Widget.DeviceDefault.Button.ButtonBar.AlertDialog.Negative" parent="Widget.DeviceDefault.Button.ButtonBar.AlertDialog">
+        <item name="background">@drawable/dialog_alert_button_negative</item>
+        <item name="minWidth">@dimen/dialog_btn_negative_width</item>
+        <item name="minHeight">@dimen/dialog_btn_negative_height</item>
+        <item name="maxWidth">@dimen/dialog_btn_negative_width</item>
+        <item name="maxHeight">@dimen/dialog_btn_negative_height</item>
+    </style>
 </resources>
\ No newline at end of file
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index e7ebb6c..22f80bd 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -1943,6 +1943,8 @@
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"已停用"</string>
     <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">"、 "</string>
     <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> - <xliff:g id="END">%2$s</xliff:g>"</string>
+    <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
+    <skip />
     <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"所有日历"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g>正在将某些音效设为静音"</string>
     <string name="system_error_wipe_data" msgid="5910572292172208493">"您的设备内部出现了问题。如果不将设备恢复出厂设置,设备运行可能会不稳定。"</string>
@@ -2424,6 +2426,14 @@
     <string name="satellite_notification_manual_summary" msgid="901206289846283445">"即使没有移动网络或 WLAN 网络,也能收发消息"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"打开“信息”应用"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"运作方式"</string>
+    <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
+    <skip />
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"待归档…"</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"重新设置指纹解锁功能"</string>
     <string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"系统无法再识别<xliff:g id="FINGERPRINT">%s</xliff:g>。"</string>
diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml
index 8f7e17c..3f4c175 100644
--- a/core/res/res/values-zh-rHK/strings.xml
+++ b/core/res/res/values-zh-rHK/strings.xml
@@ -1943,6 +1943,8 @@
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"已關閉"</string>
     <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">"、 "</string>
     <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> - <xliff:g id="END">%2$s</xliff:g>"</string>
+    <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
+    <skip />
     <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"任何日曆"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g>正將某些音效設為靜音"</string>
     <string name="system_error_wipe_data" msgid="5910572292172208493">"你裝置的系統發生問題,回復原廠設定後即可解決該問題。"</string>
@@ -2424,6 +2426,14 @@
     <string name="satellite_notification_manual_summary" msgid="901206289846283445">"在沒有流動網絡或 Wi-Fi 網絡的情況下收發短訊"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"開啟「訊息」"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"運作方式"</string>
+    <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
+    <skip />
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"待處理…"</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"重新設定「指紋解鎖」功能"</string>
     <string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"無法再辨識<xliff:g id="FINGERPRINT">%s</xliff:g>。"</string>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index 76c581c..90cede8 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -1943,6 +1943,8 @@
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"已停用"</string>
     <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">"、 "</string>
     <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> - <xliff:g id="END">%2$s</xliff:g>"</string>
+    <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
+    <skip />
     <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"任何日曆"</string>
     <string name="muted_by" msgid="91464083490094950">"「<xliff:g id="THIRD_PARTY">%1$s</xliff:g>」正在關閉部分音效"</string>
     <string name="system_error_wipe_data" msgid="5910572292172208493">"你的裝置發生內部問題,必須將裝置恢復原廠設定才能解除不穩定狀態。"</string>
@@ -2424,6 +2426,14 @@
     <string name="satellite_notification_manual_summary" msgid="901206289846283445">"即使沒有行動或 Wi-Fi 網路,還是可以收發訊息"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"開啟「訊息」應用程式"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"運作方式"</string>
+    <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
+    <skip />
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"待處理…"</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"重新設定指紋解鎖"</string>
     <string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"系統無法再辨識「<xliff:g id="FINGERPRINT">%s</xliff:g>」。"</string>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index 5aab13b..5448fcd 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -331,7 +331,7 @@
     <string name="permgroupdesc_readMediaAural" msgid="7565467343667089595">"finyelela umculo nomsindo kudivayisi yakho"</string>
     <string name="permgrouplab_readMediaVisual" msgid="4724874717811908660">"Izithombe namavidiyo"</string>
     <string name="permgroupdesc_readMediaVisual" msgid="4080463241903508688">"finyelela izithombe namavidiyo kudivayisi yakho"</string>
-    <string name="permgrouplab_microphone" msgid="2480597427667420076">"I-Microphone"</string>
+    <string name="permgrouplab_microphone" msgid="2480597427667420076">"IMicrophone"</string>
     <string name="permgroupdesc_microphone" msgid="1047786732792487722">"rekhoda ividiyo"</string>
     <string name="permgrouplab_activityRecognition" msgid="3324466667921775766">"Umsebenzi womzimba"</string>
     <string name="permgroupdesc_activityRecognition" msgid="4725624819457670704">"finyelela kumsebenzi wakho womzimba"</string>
@@ -1943,6 +1943,8 @@
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Kuvaliwe"</string>
     <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
     <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g>, <xliff:g id="END">%2$s</xliff:g>"</string>
+    <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
+    <skip />
     <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Noma iyiphi ikhalenda"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> ithulisa eminye imisindo"</string>
     <string name="system_error_wipe_data" msgid="5910572292172208493">"Kukhona inkinga yangaphakathi ngedivayisi yakho, futhi ingase ibe engazinzile kuze kube yilapho usetha kabusha yonke idatha."</string>
@@ -2424,6 +2426,14 @@
     <string name="satellite_notification_manual_summary" msgid="901206289846283445">"Thumela futhi wamukele imilayezo ngaphandle kwenethiwekhi yeselula noma yeWiFi"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Vula Imilayezo"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Indlela esebenza ngayo"</string>
+    <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
+    <skip />
+    <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
+    <skip />
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Ilindile..."</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Setha Ukuvula ngesigxivizo somunwe futhi"</string>
     <string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"I-<xliff:g id="FINGERPRINT">%s</xliff:g> angeke isaziwa."</string>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 02f9f3c..d750ff6 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -5731,6 +5731,14 @@
         </attr>
     </declare-styleable>
 
+    <!-- @hide internal use only -->
+    <declare-styleable name="NotificationProgressBar">
+        <!-- Draws the tracker on a NotificationProgressBar. -->
+        <attr name="tracker" format="reference" />
+        <!-- Height of the tracker. -->
+        <attr name="trackerHeight" format="dimension" />
+    </declare-styleable>
+
     <declare-styleable name="StackView">
         <!-- Color of the res-out outline. -->
         <attr name="resOutColor" format="color" />
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index b55e5f0..ac9bb93 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -4734,6 +4734,11 @@
    -->
     <string name="config_defaultWearableSensingService" translatable="false"></string>
 
+    <!-- The maximum number of concurrent connections allowed between the WearableSensingService and
+        wearable devices.
+        -->
+    <integer name="config_maxWearableSensingServiceConcurrentConnections">5</integer>
+
 
     <!-- The component name for the default system on-device intelligence service, -->
     <string name="config_defaultOnDeviceIntelligenceService" translatable="false"></string>
@@ -7138,6 +7143,10 @@
          root TaskDisplayArea is set to WINDOWING_MODE_FREEFORM. -->
     <bool name="config_enterDesktopByDefaultOnFreeformDisplay">false</bool>
 
+    <!-- Whether a desktop window should be maximized when it's dragged to the top edge of the
+         screen. -->
+    <bool name="config_dragToMaximizeInDesktopMode">false</bool>
+
     <!-- Frame rate compatibility value for Wallpaper
          FRAME_RATE_COMPATIBILITY_MIN (102) is used by default for lower power consumption -->
     <integer name="config_wallpaperFrameRateCompatibility">102</integer>
diff --git a/core/res/res/values/config_telephony.xml b/core/res/res/values/config_telephony.xml
index b5892f6..779422a 100644
--- a/core/res/res/values/config_telephony.xml
+++ b/core/res/res/values/config_telephony.xml
@@ -472,4 +472,10 @@
     <integer name="config_mt_sms_polling_throttle_millis">300000</integer>
     <java-symbol type="integer" name="config_mt_sms_polling_throttle_millis" />
 
+
+    <!-- The receiver class of the intent that hidden menu sends to start satellite non-emergency mode -->
+    <string name="config_satellite_carrier_roaming_non_emergency_session_class" translatable="false"></string>
+    <java-symbol type="string" name="config_satellite_carrier_roaming_non_emergency_session_class" />
+
+
 </resources>
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 522dcfa..db75206 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -820,17 +820,17 @@
     <!-- The gap between segments in the notification progress bar -->
     <dimen name="notification_progress_segSeg_gap">2dp</dimen>
     <!-- The gap between a segment and a point in the notification progress bar -->
-    <dimen name="notification_progress_segPoint_gap">4dp</dimen>
+    <dimen name="notification_progress_segPoint_gap">8dp</dimen>
     <!-- The dash gap of the notification progress bar segments -->
-    <dimen name="notification_progress_segments_dash_gap">9dp</dimen>
+    <dimen name="notification_progress_segments_dash_gap">8dp</dimen>
     <!-- The dash width of the notification progress bar segments -->
     <dimen name="notification_progress_segments_dash_width">3dp</dimen>
     <!-- The height of the notification progress bar segments -->
     <dimen name="notification_progress_segments_height">6dp</dimen>
     <!-- The radius of the notification progress bar points -->
-    <dimen name="notification_progress_points_radius">10dp</dimen>
+    <dimen name="notification_progress_points_radius">6dp</dimen>
     <!-- The corner radius of the notification progress bar points drawn as rects -->
-    <dimen name="notification_progress_points_corner_radius">4dp</dimen>
+    <dimen name="notification_progress_points_corner_radius">2dp</dimen>
     <!-- The inset of the notification progress bar points drawn as rects -->
     <dimen name="notification_progress_points_inset">0dp</dimen>
 
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 7aca535..0d4870b 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -5348,6 +5348,8 @@
     <string name="zen_mode_trigger_summary_divider_text">,\u0020</string>
     <!-- [CHAR LIMIT=40] General template for a symbolic start - end range in a text summary, used for the trigger description of a Zen mode -->
     <string name="zen_mode_trigger_summary_range_symbol_combination"><xliff:g id="start" example="Sun">%1$s</xliff:g> - <xliff:g id="end" example="Thu">%2$s</xliff:g></string>
+    <!-- [CHAR LIMIT=40] General template for a start - end range in a text summary, used for the trigger description of a Zen mode -->
+    <string name="zen_mode_trigger_summary_range_words"><xliff:g id="start" example="Sunday">%1$s</xliff:g> to <xliff:g id="end" example="Thursday">%2$s</xliff:g></string>
     <!-- [CHAR LIMIT=40] Event-based rule calendar option value for any calendar, used for the trigger description of a Zen mode -->
     <string name="zen_mode_trigger_event_calendar_any">Any calendar</string>
 
@@ -6508,6 +6510,14 @@
     <string name="satellite_notification_open_message">Open Messages</string>
     <!-- Invoke Satellite setting activity of Settings -->
     <string name="satellite_notification_how_it_works">How it works</string>
+    <!-- Popup title when satellite start with manual selection -->
+    <string name="satellite_manual_selection_state_popup_title">Turn on \"Automatically select network\"</string>
+    <!-- Popup message when satellite start with manual selection -->
+    <string name="satellite_manual_selection_state_popup_message">Turn on \"Automatically select network\" in Settings so your phone can find a network that works with satellite</string>
+    <!-- Popup button for set network selection automatic -->
+    <string name="satellite_manual_selection_state_popup_ok">Turn on</string>
+    <!-- Popup button for cancel satellite request -->
+    <string name="satellite_manual_selection_state_popup_cancel">Go back</string>
     <!-- Initial/System provided label shown for an app which gets unarchived. [CHAR LIMIT=64]. -->
     <string name="unarchival_session_app_label">Pending...</string>
 
diff --git a/core/res/res/values/styles_material.xml b/core/res/res/values/styles_material.xml
index cb8e4aa..73681d2 100644
--- a/core/res/res/values/styles_material.xml
+++ b/core/res/res/values/styles_material.xml
@@ -504,6 +504,7 @@
 
     <style name="Widget.Material.Notification.NotificationProgressBar" parent="Widget.Material.Light.ProgressBar.Horizontal">
         <item name="progressDrawable">@drawable/notification_progress</item>
+        <item name="trackerHeight">@dimen/notification_progress_tracker_height</item>
     </style>
 
     <style name="Widget.Material.Notification.Text" parent="Widget.Material.Light.TextView">
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index ecc3afe..51c405ae 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2657,6 +2657,7 @@
   <java-symbol type="string" name="zen_mode_implicit_deactivated" />
   <java-symbol type="string" name="zen_mode_trigger_summary_divider_text" />
   <java-symbol type="string" name="zen_mode_trigger_summary_range_symbol_combination" />
+  <java-symbol type="string" name="zen_mode_trigger_summary_range_words" />
   <java-symbol type="string" name="zen_mode_trigger_event_calendar_any" />
 
   <java-symbol type="string" name="display_rotation_camera_compat_toast_after_rotation" />
@@ -3988,6 +3989,7 @@
   <java-symbol type="string" name="config_ambientContextPackageNameExtraKey" />
   <java-symbol type="string" name="config_ambientContextEventArrayExtraKey" />
   <java-symbol type="string" name="config_defaultWearableSensingService" />
+  <java-symbol type="integer" name="config_maxWearableSensingServiceConcurrentConnections" />
   <java-symbol type="string" name="config_defaultOnDeviceIntelligenceService" />
   <java-symbol type="string" name="config_defaultOnDeviceSandboxedInferenceService" />
   <java-symbol type="string" name="config_onDeviceIntelligenceModelLoadedBroadcastKey" />
@@ -5542,6 +5544,10 @@
   <java-symbol type="string" name="satellite_notification_manual_summary" />
   <java-symbol type="string" name="satellite_notification_open_message" />
   <java-symbol type="string" name="satellite_notification_how_it_works" />
+  <java-symbol type="string" name="satellite_manual_selection_state_popup_title" />
+  <java-symbol type="string" name="satellite_manual_selection_state_popup_message" />
+  <java-symbol type="string" name="satellite_manual_selection_state_popup_ok" />
+  <java-symbol type="string" name="satellite_manual_selection_state_popup_cancel" />
   <java-symbol type="drawable" name="ic_satellite_alt_24px" />
   <java-symbol type="drawable" name="ic_android_satellite_24px" />
 
@@ -5571,6 +5577,10 @@
        root TaskDisplayArea is set to WINDOWING_MODE_FREEFORM. -->
   <java-symbol type="bool" name="config_enterDesktopByDefaultOnFreeformDisplay" />
 
+  <!-- Whether a desktop window should be maximized when it's dragged to the top edge of the
+       screen. -->
+  <java-symbol type="bool" name="config_dragToMaximizeInDesktopMode" />
+
   <!-- Frame rate compatibility value for Wallpaper -->
   <java-symbol type="integer" name="config_wallpaperFrameRateCompatibility" />
 
diff --git a/core/tests/BroadcastRadioTests/src/android/hardware/radio/RadioAlertUnitTest.java b/core/tests/BroadcastRadioTests/src/android/hardware/radio/RadioAlertUnitTest.java
new file mode 100644
index 0000000..9cfb9af
--- /dev/null
+++ b/core/tests/BroadcastRadioTests/src/android/hardware/radio/RadioAlertUnitTest.java
@@ -0,0 +1,609 @@
+/*
+ * 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.radio;
+
+import static org.junit.Assert.assertThrows;
+
+import android.os.Parcel;
+import android.platform.test.annotations.EnableFlags;
+
+import com.google.common.primitives.Ints;
+import com.google.common.truth.Expect;
+
+import org.junit.Rule;
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@EnableFlags(Flags.FLAG_HD_RADIO_EMERGENCY_ALERT_SYSTEM)
+public final class RadioAlertUnitTest {
+
+    private static final int TEST_FLAGS = 0;
+    private static final int CREATOR_ARRAY_SIZE = 3;
+    private static final int TEST_STATUS = RadioAlert.STATUS_ACTUAL;
+    private static final int TEST_TYPE = RadioAlert.MESSAGE_TYPE_ALERT;
+    private static final int[] TEST_CATEGORIES_1 = new int[]{RadioAlert.CATEGORY_CBRNE,
+            RadioAlert.CATEGORY_GEO};
+    private static final int[] TEST_CATEGORIES_2 = new int[]{RadioAlert.CATEGORY_CBRNE,
+            RadioAlert.CATEGORY_FIRE};
+    private static final int TEST_URGENCY_1 = RadioAlert.URGENCY_EXPECTED;
+    private static final int TEST_URGENCY_2 = RadioAlert.URGENCY_FUTURE;
+    private static final int TEST_SEVERITY_1 = RadioAlert.SEVERITY_SEVERE;
+    private static final int TEST_SEVERITY_2 = RadioAlert.SEVERITY_MODERATE;
+    private static final int TEST_CERTAINTY_1 = RadioAlert.CERTAINTY_POSSIBLE;
+    private static final int TEST_CERTAINTY_2 = RadioAlert.CERTAINTY_UNLIKELY;
+    private static final String TEST_DESCRIPTION_MESSAGE_1 = "Test Alert Description Message 1.";
+    private static final String TEST_DESCRIPTION_MESSAGE_2 = "Test Alert Description Message 2.";
+    private static final String TEST_GEOCODE_VALUE_NAME = "SAME";
+    private static final String TEST_GEOCODE_VALUE_1 = "006109";
+    private static final String TEST_GEOCODE_VALUE_2 = "006009";
+    private static final double TEST_POLYGON_LATITUDE_START = -38.47;
+    private static final double TEST_POLYGON_LONGITUDE_START = -120.14;
+    private static final RadioAlert.Coordinate TEST_POLYGON_COORDINATE_START =
+            new RadioAlert.Coordinate(TEST_POLYGON_LATITUDE_START, TEST_POLYGON_LONGITUDE_START);
+    private static final List<RadioAlert.Coordinate> TEST_COORDINATES = List.of(
+            TEST_POLYGON_COORDINATE_START, new RadioAlert.Coordinate(38.34, -119.95),
+            new RadioAlert.Coordinate(38.52, -119.74), new RadioAlert.Coordinate(38.62, -119.89),
+            TEST_POLYGON_COORDINATE_START);
+    private static final RadioAlert.Polygon TEST_POLYGON = new RadioAlert.Polygon(TEST_COORDINATES);
+    private static final RadioAlert.Geocode TEST_GEOCODE_1 = new RadioAlert.Geocode(
+            TEST_GEOCODE_VALUE_NAME, TEST_GEOCODE_VALUE_1);
+    private static final RadioAlert.Geocode TEST_GEOCODE_2 = new RadioAlert.Geocode(
+            TEST_GEOCODE_VALUE_NAME, TEST_GEOCODE_VALUE_2);
+    private static final RadioAlert.AlertArea TEST_AREA_1 = new RadioAlert.AlertArea(
+            List.of(TEST_POLYGON), List.of(TEST_GEOCODE_1));
+    private static final RadioAlert.AlertArea TEST_AREA_2 = new RadioAlert.AlertArea(
+            new ArrayList<>(), List.of(TEST_GEOCODE_1, TEST_GEOCODE_2));
+    private static final List<RadioAlert.AlertArea> TEST_AREA_LIST_1 = List.of(TEST_AREA_1);
+    private static final List<RadioAlert.AlertArea> TEST_AREA_LIST_2 = List.of(TEST_AREA_2);
+    private static final String TEST_LANGUAGE_1 = "en-US";
+
+    private static final RadioAlert.AlertInfo TEST_ALERT_INFO_1 = new RadioAlert.AlertInfo(
+            TEST_CATEGORIES_1, TEST_URGENCY_1, TEST_SEVERITY_1, TEST_CERTAINTY_1,
+            TEST_DESCRIPTION_MESSAGE_1, TEST_AREA_LIST_1, TEST_LANGUAGE_1);
+    private static final RadioAlert.AlertInfo TEST_ALERT_INFO_2 = new RadioAlert.AlertInfo(
+            TEST_CATEGORIES_2, TEST_URGENCY_2, TEST_SEVERITY_2, TEST_CERTAINTY_2,
+            TEST_DESCRIPTION_MESSAGE_2, TEST_AREA_LIST_2, /* language= */ null);
+    private static final RadioAlert TEST_ALERT = new RadioAlert(TEST_STATUS, TEST_TYPE,
+            List.of(TEST_ALERT_INFO_1, TEST_ALERT_INFO_2));
+
+    @Rule
+    public final Expect mExpect = Expect.create();
+
+    @Test
+    public void constructor_withNullValueName_forGeocode_fails() {
+        NullPointerException thrown = assertThrows(NullPointerException.class, () ->
+                new RadioAlert.Geocode(/* valueName= */ null, TEST_GEOCODE_VALUE_1));
+
+        mExpect.withMessage("Exception for geocode constructor with null value name")
+                .that(thrown).hasMessageThat()
+                .contains("Geocode value name can not be null");
+    }
+
+    @Test
+    public void constructor_withNullValue_forGeocode_fails() {
+        NullPointerException thrown = assertThrows(NullPointerException.class, () ->
+                new RadioAlert.Geocode(TEST_GEOCODE_VALUE_NAME, /* value= */ null));
+
+        mExpect.withMessage("Exception for geocode constructor with null value")
+                .that(thrown).hasMessageThat()
+                .contains("Geocode value can not be null");
+    }
+
+    @Test
+    public void getValueName_forGeocode() {
+        mExpect.withMessage("Value name of geocode").that(TEST_GEOCODE_1.getValueName())
+                .isEqualTo(TEST_GEOCODE_VALUE_NAME);
+    }
+
+    @Test
+    public void getValue_forGeocode() {
+        mExpect.withMessage("Value of geocode").that(TEST_GEOCODE_1.getValue())
+                .isEqualTo(TEST_GEOCODE_VALUE_1);
+    }
+
+    @Test
+    public void describeContents_forGeocode() {
+        mExpect.withMessage("Contents of geocode")
+                .that(TEST_GEOCODE_1.describeContents()).isEqualTo(0);
+    }
+
+    @Test
+    public void writeToParcel_forGeocode() {
+        Parcel parcel = Parcel.obtain();
+
+        TEST_GEOCODE_1.writeToParcel(parcel, TEST_FLAGS);
+
+        parcel.setDataPosition(0);
+        RadioAlert.Geocode geocodeFromParcel = RadioAlert.Geocode.CREATOR.createFromParcel(parcel);
+        mExpect.withMessage("Geocode from parcel").that(geocodeFromParcel)
+                .isEqualTo(TEST_GEOCODE_1);
+    }
+
+    @Test
+    public void newArray_forGeocodeCreator() {
+        RadioAlert.Geocode[] geocodes = RadioAlert.Geocode.CREATOR.newArray(CREATOR_ARRAY_SIZE);
+
+        mExpect.withMessage("Geocodes").that(geocodes).hasLength(CREATOR_ARRAY_SIZE);
+    }
+
+    @Test
+    public void hashCode_withSameGeocodes() {
+        RadioAlert.Geocode geocodeCompared = new RadioAlert.Geocode(TEST_GEOCODE_VALUE_NAME,
+                TEST_GEOCODE_VALUE_1);
+
+        mExpect.withMessage("Hash code of the same gecode")
+                .that(geocodeCompared.hashCode()).isEqualTo(TEST_GEOCODE_1.hashCode());
+    }
+
+    @Test
+    public void equals_withDifferentGeocodes() {
+        mExpect.withMessage("Different geocode").that(TEST_GEOCODE_1)
+                .isNotEqualTo(TEST_GEOCODE_2);
+    }
+
+    @Test
+    @SuppressWarnings("TruthIncompatibleType")
+    public void equals_withDifferentTypeObject_forGeocode() {
+        mExpect.withMessage("Non-geocode object").that(TEST_GEOCODE_1)
+                .isNotEqualTo(TEST_POLYGON_COORDINATE_START);
+    }
+
+    @Test
+    public void constructor_withInvalidLatitude_forCoordinate_fails() {
+        IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class, () ->
+                new RadioAlert.Coordinate(/* latitude= */ -92.0, TEST_POLYGON_LONGITUDE_START));
+
+        mExpect.withMessage("Exception for coordinate constructor with invalid latitude")
+                .that(thrown).hasMessageThat().contains("Latitude");
+    }
+
+    @Test
+    public void constructor_withInvalidLongitude_forCoordinate_fails() {
+        IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class, () ->
+                new RadioAlert.Coordinate(TEST_POLYGON_LATITUDE_START, /* longitude= */ 200.0));
+
+        mExpect.withMessage("Exception for coordinate constructor with invalid longitude")
+                .that(thrown).hasMessageThat().contains("Longitude");
+    }
+
+    @Test
+    public void getLatitude_forCoordinate() {
+        mExpect.withMessage("Latitude of coordinate")
+                .that(TEST_POLYGON_COORDINATE_START.getLatitude())
+                .isEqualTo(TEST_POLYGON_LATITUDE_START);
+    }
+
+    @Test
+    public void getLongitude_forCoordinate() {
+        mExpect.withMessage("Longitude of coordinate")
+                .that(TEST_POLYGON_COORDINATE_START.getLongitude())
+                .isEqualTo(TEST_POLYGON_LONGITUDE_START);
+    }
+
+    @Test
+    public void describeContents_forCoordinate() {
+        mExpect.withMessage("Contents of coordinate")
+                .that(TEST_POLYGON_COORDINATE_START.describeContents()).isEqualTo(0);
+    }
+
+    @Test
+    public void writeToParcel_forCoordinate() {
+        Parcel parcel = Parcel.obtain();
+
+        TEST_POLYGON_COORDINATE_START.writeToParcel(parcel, TEST_FLAGS);
+
+        parcel.setDataPosition(0);
+        RadioAlert.Coordinate coordinateFromParcel = RadioAlert.Coordinate.CREATOR
+                .createFromParcel(parcel);
+        mExpect.withMessage("Coordinate from parcel").that(coordinateFromParcel)
+                .isEqualTo(TEST_POLYGON_COORDINATE_START);
+    }
+
+    @Test
+    public void newArray_forCoordinateCreator() {
+        RadioAlert.Coordinate[] coordinates = RadioAlert.Coordinate.CREATOR
+                .newArray(CREATOR_ARRAY_SIZE);
+
+        mExpect.withMessage("Coordinates").that(coordinates).hasLength(CREATOR_ARRAY_SIZE);
+    }
+
+    @Test
+    public void hashCode_withSameCoordinates() {
+        RadioAlert.Coordinate coordinateCompared = new RadioAlert.Coordinate(
+                TEST_POLYGON_LATITUDE_START, TEST_POLYGON_LONGITUDE_START);
+
+        mExpect.withMessage("Hash code of the same coordinate")
+                .that(coordinateCompared.hashCode())
+                .isEqualTo(TEST_POLYGON_COORDINATE_START.hashCode());
+    }
+
+    @Test
+    public void equals_withDifferentCoordinates() {
+        mExpect.withMessage("Different coordinate").that(TEST_POLYGON_COORDINATE_START)
+                .isNotEqualTo(TEST_COORDINATES.get(1));
+    }
+
+    @Test
+    @SuppressWarnings("TruthIncompatibleType")
+    public void equals_withDifferentTypeObject_forCoordinate() {
+        mExpect.withMessage("Non-coordinate object").that(TEST_POLYGON_COORDINATE_START)
+                .isNotEqualTo(TEST_GEOCODE_1);
+    }
+
+    @Test
+    public void constructor_withNullCoordinates_forPolygon_fails() {
+        NullPointerException thrown = assertThrows(NullPointerException.class, () ->
+                new RadioAlert.Polygon(/* coordinates= */ null));
+
+        mExpect.withMessage("Exception for polygon constructor with null coordinates")
+                .that(thrown).hasMessageThat().contains("Coordinates can not be null");
+    }
+
+    @Test
+    public void constructor_withLessThanFourCoordinates_forPolygon_fails() {
+        IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class, () ->
+                new RadioAlert.Polygon(List.of(TEST_POLYGON_COORDINATE_START,
+                        TEST_POLYGON_COORDINATE_START)));
+
+        mExpect.withMessage("Exception for polygon constructor with less than four coordinates")
+                .that(thrown).hasMessageThat().contains("Number of coordinates must be at least 4");
+    }
+
+    @Test
+    public void constructor_withDifferentFirstAndLastCoordinates_forPolygon_fails() {
+        IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class, () ->
+                new RadioAlert.Polygon(List.of(TEST_POLYGON_COORDINATE_START,
+                        new RadioAlert.Coordinate(38.34, -119.95),
+                        new RadioAlert.Coordinate(38.52, -119.74),
+                        new RadioAlert.Coordinate(38.62, -119.89))));
+
+        mExpect.withMessage(
+                "Exception for polygon constructor with different first and last coordinates")
+                .that(thrown).hasMessageThat().contains(
+                        "last and first coordinates must be the same");
+    }
+
+    @Test
+    public void getCoordinates_forPolygon() {
+        mExpect.withMessage("Coordinates in polygon").that(TEST_POLYGON.getCoordinates())
+                .containsExactlyElementsIn(TEST_COORDINATES);
+    }
+
+    @Test
+    public void describeContents_forPolygon() {
+        mExpect.withMessage("Contents of polygon")
+                .that(TEST_POLYGON.describeContents()).isEqualTo(0);
+    }
+
+    @Test
+    public void writeToParcel_forPolygon() {
+        Parcel parcel = Parcel.obtain();
+
+        TEST_POLYGON.writeToParcel(parcel, TEST_FLAGS);
+
+        parcel.setDataPosition(0);
+        RadioAlert.Polygon polygonFromParcel = RadioAlert.Polygon.CREATOR.createFromParcel(parcel);
+        mExpect.withMessage("Polygon from parcel").that(polygonFromParcel)
+                .isEqualTo(TEST_POLYGON);
+    }
+
+    @Test
+    public void newArray_forPolygonCreator() {
+        RadioAlert.Polygon[] polygons = RadioAlert.Polygon.CREATOR.newArray(CREATOR_ARRAY_SIZE);
+
+        mExpect.withMessage("Polygons").that(polygons).hasLength(CREATOR_ARRAY_SIZE);
+    }
+
+    @Test
+    public void hashCode_withSamePolygons() {
+        RadioAlert.Polygon polygonCompared = new RadioAlert.Polygon(TEST_COORDINATES);
+
+        mExpect.withMessage("Hash code of the same polygon")
+                .that(polygonCompared.hashCode()).isEqualTo(TEST_POLYGON.hashCode());
+    }
+
+    @Test
+    @SuppressWarnings("TruthIncompatibleType")
+    public void equals_withDifferentTypeObject_forPolygon() {
+        mExpect.withMessage("Non-polygon object").that(TEST_POLYGON)
+                .isNotEqualTo(TEST_GEOCODE_1);
+    }
+
+    @Test
+    public void constructor_withNullPolygons_forAlertArea_fails() {
+        NullPointerException thrown = assertThrows(NullPointerException.class, () ->
+                new RadioAlert.AlertArea(/* polygons= */ null, List.of(TEST_GEOCODE_1)));
+
+        mExpect.withMessage("Exception for alert area constructor with null polygon list")
+                .that(thrown).hasMessageThat().contains("Polygons can not be null");
+    }
+
+    @Test
+    public void constructor_withNullGeocodes_forAlertArea_fails() {
+        NullPointerException thrown = assertThrows(NullPointerException.class, () ->
+                new RadioAlert.AlertArea(List.of(TEST_POLYGON), /* geocodes= */ null));
+
+        mExpect.withMessage("Exception for alert area constructor with null geocode list")
+                .that(thrown).hasMessageThat().contains("Geocodes can not be null");
+    }
+
+    @Test
+    public void getPolygons_forAlertArea() {
+        mExpect.withMessage("Polygons in alert area").that(TEST_AREA_1.getPolygons())
+                .containsExactly(TEST_POLYGON);
+    }
+
+    @Test
+    public void getGeocodes_forAlertArea() {
+        mExpect.withMessage("Polygons in alert area").that(TEST_AREA_2.getGeocodes())
+                .containsExactly(TEST_GEOCODE_1, TEST_GEOCODE_2);
+    }
+
+    @Test
+    public void describeContents_forAlertArea() {
+        mExpect.withMessage("Contents of alert area")
+                .that(TEST_AREA_1.describeContents()).isEqualTo(0);
+    }
+
+    @Test
+    public void writeToParcel_forAlertArea() {
+        Parcel parcel = Parcel.obtain();
+
+        TEST_AREA_1.writeToParcel(parcel, TEST_FLAGS);
+
+        parcel.setDataPosition(0);
+        RadioAlert.AlertArea areaFromParcel = RadioAlert.AlertArea.CREATOR.createFromParcel(parcel);
+        mExpect.withMessage("Alert area from parcel").that(areaFromParcel)
+                .isEqualTo(TEST_AREA_1);
+    }
+
+    @Test
+    public void newArray_forAlertAreaCreator() {
+        RadioAlert.AlertArea[] alertAreas = RadioAlert.AlertArea.CREATOR
+                .newArray(CREATOR_ARRAY_SIZE);
+
+        mExpect.withMessage("Alert areas").that(alertAreas).hasLength(CREATOR_ARRAY_SIZE);
+    }
+
+    @Test
+    public void hashCode_withSameAlertAreas() {
+        RadioAlert.AlertArea alertAreaCompared = new RadioAlert.AlertArea(List.of(TEST_POLYGON),
+                List.of(TEST_GEOCODE_1));
+
+        mExpect.withMessage("Hash code of the same alert area")
+                .that(alertAreaCompared.hashCode()).isEqualTo(TEST_AREA_1.hashCode());
+    }
+
+    @Test
+    public void equals_withDifferentAlertAreas() {
+        mExpect.withMessage("Different alert area").that(TEST_AREA_1).isNotEqualTo(TEST_AREA_2);
+    }
+
+    @Test
+    @SuppressWarnings("TruthIncompatibleType")
+    public void equals_withDifferentTypeObject_forAlertArea() {
+        mExpect.withMessage("Non-alert-area object").that(TEST_AREA_1)
+                .isNotEqualTo(TEST_GEOCODE_1);
+    }
+
+    @Test
+    public void constructor_withNullCategories_forAlertInfo_fails() {
+        NullPointerException thrown = assertThrows(NullPointerException.class, () ->
+                new RadioAlert.AlertInfo(/* categories= */ null, TEST_URGENCY_1, TEST_SEVERITY_1,
+                        TEST_CERTAINTY_1, TEST_DESCRIPTION_MESSAGE_1, TEST_AREA_LIST_1,
+                        TEST_LANGUAGE_1));
+
+        mExpect.withMessage("Exception for alert info constructor with null categories")
+                .that(thrown).hasMessageThat().contains("Categories can not be null");
+    }
+
+    @Test
+    public void constructor_withNullAreaList_forAlertInfo_fails() {
+        NullPointerException thrown = assertThrows(NullPointerException.class, () ->
+                new RadioAlert.AlertInfo(TEST_CATEGORIES_1, TEST_URGENCY_1, TEST_SEVERITY_1,
+                        TEST_CERTAINTY_1, TEST_DESCRIPTION_MESSAGE_1, /* areaList= */ null,
+                        TEST_LANGUAGE_1));
+
+        mExpect.withMessage("Exception for alert info constructor with null area list")
+                .that(thrown).hasMessageThat().contains("Area list can not be null");
+    }
+
+    @Test
+    public void getCategories_forAlertInfo() {
+        mExpect.withMessage("Radio alert info categories")
+                .that(Ints.asList(TEST_ALERT_INFO_1.getCategories()))
+                .containsExactlyElementsIn(Ints.asList(TEST_CATEGORIES_1));
+    }
+
+    @Test
+    public void getUrgency_forAlertInfo() {
+        mExpect.withMessage("Radio alert info urgency")
+                .that(TEST_ALERT_INFO_1.getUrgency()).isEqualTo(TEST_URGENCY_1);
+    }
+
+    @Test
+    public void getSeverity_forAlertInfo() {
+        mExpect.withMessage("Radio alert info severity")
+                .that(TEST_ALERT_INFO_1.getSeverity()).isEqualTo(TEST_SEVERITY_1);
+    }
+
+    @Test
+    public void getCertainty_forAlertInfo() {
+        mExpect.withMessage("Radio alert info certainty")
+                .that(TEST_ALERT_INFO_1.getCertainty()).isEqualTo(TEST_CERTAINTY_1);
+    }
+
+    @Test
+    public void getDescription_forAlertInfo() {
+        mExpect.withMessage("Radio alert info description")
+                .that(TEST_ALERT_INFO_1.getDescription()).isEqualTo(TEST_DESCRIPTION_MESSAGE_1);
+    }
+
+    @Test
+    public void getAreas_forAlertInfo() {
+        mExpect.withMessage("Radio alert info areas")
+                .that(TEST_ALERT_INFO_1.getAreas()).containsAtLeastElementsIn(TEST_AREA_LIST_1);
+    }
+
+    @Test
+    public void getLanguage_forAlertInfo() {
+        mExpect.withMessage("Radio alert language")
+                .that(TEST_ALERT_INFO_1.getLanguage()).isEqualTo(TEST_LANGUAGE_1);
+    }
+
+    @Test
+    public void describeContents_forAlertInfo() {
+        mExpect.withMessage("Contents of alert info")
+                .that(TEST_ALERT_INFO_1.describeContents()).isEqualTo(0);
+    }
+
+    @Test
+    public void writeToParcel_forAlertInfoWithNullLanguage() {
+        Parcel parcel = Parcel.obtain();
+
+        TEST_ALERT_INFO_2.writeToParcel(parcel, TEST_FLAGS);
+
+        parcel.setDataPosition(0);
+        RadioAlert.AlertInfo alertInfoFromParcel = RadioAlert.AlertInfo.CREATOR
+                .createFromParcel(parcel);
+        mExpect.withMessage("Alert info from parcel with null language")
+                .that(alertInfoFromParcel).isEqualTo(TEST_ALERT_INFO_2);
+    }
+
+    @Test
+    public void writeToParcel_forAlertInfoWithNonnullLanguage() {
+        Parcel parcel = Parcel.obtain();
+
+        TEST_ALERT_INFO_1.writeToParcel(parcel, TEST_FLAGS);
+
+        parcel.setDataPosition(0);
+        RadioAlert.AlertInfo alertInfoFromParcel = RadioAlert.AlertInfo.CREATOR
+                .createFromParcel(parcel);
+        mExpect.withMessage("Alert info with nonnull language from parcel")
+                .that(alertInfoFromParcel).isEqualTo(TEST_ALERT_INFO_1);
+    }
+
+    @Test
+    public void newArray_forAlertInfoCreator() {
+        RadioAlert.AlertInfo[] alertInfos = RadioAlert.AlertInfo.CREATOR
+                .newArray(CREATOR_ARRAY_SIZE);
+
+        mExpect.withMessage("Alert infos").that(alertInfos).hasLength(CREATOR_ARRAY_SIZE);
+    }
+
+    @Test
+    public void hashCode_withSameAlertInfos() {
+        RadioAlert.AlertInfo alertInfoCompared = new RadioAlert.AlertInfo(
+                TEST_CATEGORIES_1, TEST_URGENCY_1, TEST_SEVERITY_1, TEST_CERTAINTY_1,
+                TEST_DESCRIPTION_MESSAGE_1, TEST_AREA_LIST_1, TEST_LANGUAGE_1);
+
+        mExpect.withMessage("Hash code of the same alert info")
+                .that(alertInfoCompared.hashCode()).isEqualTo(TEST_ALERT_INFO_1.hashCode());
+    }
+
+    @Test
+    public void constructor_forRadioAlert() {
+        NullPointerException thrown = assertThrows(NullPointerException.class, () ->
+                new RadioAlert(TEST_STATUS, TEST_TYPE, /* infoList= */ null));
+
+        mExpect.withMessage("Exception for alert constructor with null alert info list")
+                .that(thrown).hasMessageThat().contains("Alert info list can not be null");
+    }
+
+    @Test
+    public void equals_withDifferentAlertInfo() {
+        mExpect.withMessage("Different alert info").that(TEST_ALERT_INFO_1)
+                .isNotEqualTo(TEST_ALERT_INFO_2);
+    }
+
+    @Test
+    @SuppressWarnings("TruthIncompatibleType")
+    public void equals_withDifferentTypeObject_forAlertInfo() {
+        mExpect.withMessage("Non-alert-info object").that(TEST_ALERT_INFO_1)
+                .isNotEqualTo(TEST_AREA_1);
+    }
+
+    @Test
+    public void getStatus() {
+        mExpect.withMessage("Radio alert status").that(TEST_ALERT.getStatus())
+                .isEqualTo(TEST_STATUS);
+    }
+
+    @Test
+    public void getMessageType() {
+        mExpect.withMessage("Radio alert message type")
+                .that(TEST_ALERT.getMessageType()).isEqualTo(TEST_TYPE);
+    }
+
+    @Test
+    public void getInfoList() {
+        mExpect.withMessage("Radio alert info list").that(TEST_ALERT.getInfoList())
+                .containsExactly(TEST_ALERT_INFO_1, TEST_ALERT_INFO_2);
+    }
+
+    @Test
+    public void describeContents() {
+        mExpect.withMessage("Contents of radio alert")
+                .that(TEST_ALERT.describeContents()).isEqualTo(0);
+    }
+
+    @Test
+    public void writeToParcel() {
+        Parcel parcel = Parcel.obtain();
+
+        TEST_ALERT.writeToParcel(parcel, TEST_FLAGS);
+
+        parcel.setDataPosition(0);
+        RadioAlert alertFromParcel = RadioAlert.CREATOR.createFromParcel(parcel);
+        mExpect.withMessage("Alert from parcel").that(alertFromParcel)
+                .isEqualTo(TEST_ALERT);
+    }
+
+    @Test
+    public void hashCode_withSameAlerts() {
+        RadioAlert alertCompared = new RadioAlert(TEST_STATUS, TEST_TYPE,
+                List.of(TEST_ALERT_INFO_1, TEST_ALERT_INFO_2));
+
+        mExpect.withMessage("Hash code of the same alert")
+                .that(alertCompared.hashCode()).isEqualTo(TEST_ALERT.hashCode());
+    }
+
+    @Test
+    public void newArray_forAlertCreator() {
+        RadioAlert[] alerts = RadioAlert.CREATOR.newArray(CREATOR_ARRAY_SIZE);
+
+        mExpect.withMessage("Alerts").that(alerts).hasLength(CREATOR_ARRAY_SIZE);
+    }
+
+    @Test
+    public void equals_withDifferentAlert() {
+        RadioAlert differentAlert = new RadioAlert(TEST_STATUS, TEST_TYPE,
+                List.of(TEST_ALERT_INFO_2));
+
+        mExpect.withMessage("Different alert").that(TEST_ALERT)
+                .isNotEqualTo(differentAlert);
+    }
+
+    @Test
+    @SuppressWarnings("TruthIncompatibleType")
+    public void equals_withDifferentTypeObject() {
+        mExpect.withMessage("Non-alert object").that(TEST_ALERT)
+                .isNotEqualTo(TEST_ALERT_INFO_2);
+    }
+}
diff --git a/core/tests/InputMethodCoreTests/src/android/view/inputmethod/EditorInfoTest.java b/core/tests/InputMethodCoreTests/src/android/view/inputmethod/EditorInfoTest.java
index 013117e..1721e1e 100644
--- a/core/tests/InputMethodCoreTests/src/android/view/inputmethod/EditorInfoTest.java
+++ b/core/tests/InputMethodCoreTests/src/android/view/inputmethod/EditorInfoTest.java
@@ -34,6 +34,7 @@
 import android.os.Parcel;
 import android.os.UserHandle;
 import android.platform.test.annotations.Presubmit;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
 import android.text.InputType;
 import android.text.Spannable;
 import android.text.SpannableString;
@@ -87,6 +88,8 @@
         TEST_EDITOR_INFO.targetInputMethodUser = UserHandle.of(TEST_USER_ID);
     }
 
+    private final DeviceFlagsValueProvider mFlagsValueProvider = new DeviceFlagsValueProvider();
+
     /**
      * Makes sure that {@code null} {@link EditorInfo#targetInputMethodUser} can be copied via
      * {@link Parcel}.
@@ -522,7 +525,9 @@
         info.setSupportedHandwritingGestures(Arrays.asList(SelectGesture.class));
         info.setSupportedHandwritingGesturePreviews(
                 Stream.of(SelectGesture.class).collect(Collectors.toSet()));
-        if (Flags.editorinfoHandwritingEnabled()) {
+        final boolean isStylusHandwritingEnabled =
+                mFlagsValueProvider.getBoolean(Flags.FLAG_EDITORINFO_HANDWRITING_ENABLED);
+        if (isStylusHandwritingEnabled) {
             info.setStylusHandwritingEnabled(true);
         }
         info.packageName = "android.view.inputmethod";
@@ -548,8 +553,7 @@
                         + "prefix2: hintLocales=[en,es,zh]\n"
                         + "prefix2: supportedHandwritingGestureTypes=SELECT\n"
                         + "prefix2: supportedHandwritingGesturePreviewTypes=SELECT\n"
-                        + "prefix2: isStylusHandwritingEnabled="
-                                + Flags.editorinfoHandwritingEnabled() + "\n"
+                        + "prefix2: isStylusHandwritingEnabled=" + isStylusHandwritingEnabled + "\n"
                         + "prefix2: contentMimeTypes=[image/png]\n"
                         + "prefix2: targetInputMethodUserId=10\n");
     }
diff --git a/core/tests/InputMethodCoreTests/src/android/view/inputmethod/InputMethodInfoTest.java b/core/tests/InputMethodCoreTests/src/android/view/inputmethod/InputMethodInfoTest.java
index 61bf137..44b2d90 100644
--- a/core/tests/InputMethodCoreTests/src/android/view/inputmethod/InputMethodInfoTest.java
+++ b/core/tests/InputMethodCoreTests/src/android/view/inputmethod/InputMethodInfoTest.java
@@ -26,6 +26,8 @@
 import android.content.pm.ServiceInfo;
 import android.os.Bundle;
 import android.os.Parcel;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
 import android.platform.test.flag.junit.SetFlagsRule;
 
 import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -43,8 +45,9 @@
 public class InputMethodInfoTest {
 
     @Rule
-    public SetFlagsRule mSetFlagsRule =
-            new SetFlagsRule(SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT);
+    public SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
+    private final DeviceFlagsValueProvider mFlagsValueProvider = new DeviceFlagsValueProvider();
 
     @Test
     public void testEqualsAndHashCode() throws Exception {
@@ -70,7 +73,7 @@
         assertThat(imi.supportsInlineSuggestionsWithTouchExploration(), is(false));
         assertThat(imi.supportsStylusHandwriting(), is(false));
         assertThat(imi.createStylusHandwritingSettingsActivityIntent(), equalTo(null));
-        if (Flags.imeSwitcherRevampApi()) {
+        if (mFlagsValueProvider.getBoolean(Flags.FLAG_IME_SWITCHER_REVAMP_API)) {
             assertThat(imi.createImeLanguageSettingsActivityIntent(), equalTo(null));
         }
     }
@@ -121,9 +124,8 @@
     }
 
     @Test
+    @EnableFlags(android.companion.virtual.flags.Flags.FLAG_VDM_CUSTOM_IME)
     public void testIsVirtualDeviceOnly() throws Exception {
-        mSetFlagsRule.enableFlags(android.companion.virtual.flags.Flags.FLAG_VDM_CUSTOM_IME);
-
         final InputMethodInfo imi = buildInputMethodForTest(R.xml.ime_meta_virtual_device_only);
 
         assertThat(imi.isVirtualDeviceOnly(), is(true));
diff --git a/core/tests/coretests/Android.bp b/core/tests/coretests/Android.bp
index 56e18e6..a382d79 100644
--- a/core/tests/coretests/Android.bp
+++ b/core/tests/coretests/Android.bp
@@ -146,6 +146,11 @@
         ":BinderProxyCountingTestService",
         ":AppThatUsesAppOps",
         ":AppThatCallsBinderMethods",
+        ":HelloWorldSdk1",
+        ":HelloWorldUsingSdk1AndSdk1",
+        ":HelloWorldUsingSdk1And2",
+        ":HelloWorldUsingSdkMalformedNegativeVersion",
+        ":CtsStaticSharedLibConsumerApp1",
     ],
 }
 
@@ -262,6 +267,7 @@
         "src/android/content/ContextTest.java",
         "src/android/content/pm/PackageManagerTest.java",
         "src/android/content/pm/UserInfoTest.java",
+        "src/android/app/PropertyInvalidatedCacheTests.java",
         "src/android/database/CursorWindowTest.java",
         "src/android/os/**/*.java",
         "src/android/content/res/*.java",
diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml
index da7da7d..9675d6b 100644
--- a/core/tests/coretests/AndroidManifest.xml
+++ b/core/tests/coretests/AndroidManifest.xml
@@ -46,6 +46,7 @@
     <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
     <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
     <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
+    <uses-permission android:name="android.permission.BIND_WALLPAPER"/>
     <uses-permission android:name="android.permission.BLUETOOTH" />
     <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
     <uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE"/>
@@ -1770,6 +1771,25 @@
                 <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST" />
             </intent-filter>
         </activity>
+
+        <!-- Used by WallpaperInstanceTest -->
+        <service
+            android:name="stub.StubWallpaperService"
+            android:directBootAware="true"
+            android:enabled="true"
+            android:exported="true"
+            android:label="Stub wallpaper"
+            android:permission="android.permission.BIND_WALLPAPER">
+
+            <intent-filter>
+                <action android:name="android.service.wallpaper.WallpaperService" />
+            </intent-filter>
+
+            <!-- Link to XML that defines the wallpaper info. -->
+            <meta-data
+                android:name="android.service.wallpaper"
+                android:resource="@xml/livewallpaper" />
+        </service>
     </application>
 
     <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
diff --git a/core/tests/coretests/AndroidTest.xml b/core/tests/coretests/AndroidTest.xml
index 05ab783..3f7c83a 100644
--- a/core/tests/coretests/AndroidTest.xml
+++ b/core/tests/coretests/AndroidTest.xml
@@ -29,6 +29,20 @@
         <option name="test-file-name" value="AppThatCallsBinderMethods.apk" />
     </target_preparer>
 
+    <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+        <option name="cleanup" value="true"/>
+        <option name="push-file" key="HelloWorldUsingSdk1And2.apk"
+            value="/data/local/tmp/tests/coretests/pm/HelloWorldUsingSdk1And2.apk"/>
+        <option name="push-file" key="HelloWorldUsingSdk1AndSdk1.apk"
+            value="/data/local/tmp/tests/coretests/pm/HelloWorldUsingSdk1AndSdk1.apk"/>
+        <option name="push-file" key="HelloWorldUsingSdkMalformedNegativeVersion.apk"
+            value="/data/local/tmp/tests/coretests/pm/HelloWorldUsingSdkMalformedNegativeVersion.apk"/>
+        <option name="push-file" key="HelloWorldSdk1.apk"
+            value="/data/local/tmp/tests/coretests/pm/HelloWorldSdk1.apk"/>
+        <option name="push-file" key="CtsStaticSharedLibConsumerApp1.apk"
+            value="/data/local/tmp/tests/coretests/pm/CtsStaticSharedLibConsumerApp1.apk"/>
+    </target_preparer>
+
     <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
         <!-- TODO(b/254155965): Design a mechanism to finally remove this command. -->
         <option name="run-command" value="settings put global device_config_sync_disabled 0" />
diff --git a/core/res/res/color-watch-v36/btn_material_filled_tonal_text_color.xml b/core/tests/coretests/res/xml/livewallpaper.xml
similarity index 61%
copy from core/res/res/color-watch-v36/btn_material_filled_tonal_text_color.xml
copy to core/tests/coretests/res/xml/livewallpaper.xml
index 94e50fb..3b3f4a7 100644
--- a/core/res/res/color-watch-v36/btn_material_filled_tonal_text_color.xml
+++ b/core/tests/coretests/res/xml/livewallpaper.xml
@@ -1,3 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
 <!--
   ~ Copyright (C) 2024 The Android Open Source Project
   ~
@@ -11,13 +12,9 @@
   ~ 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.
+  ~ limitations under the License
   -->
-
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:state_enabled="false"
-          android:alpha="?attr/primaryContentAlpha"
-          android:color="?attr/materialColorOnSurface" />
-    <item android:state_enabled="true"
-          android:color="?attr/materialColorOnSurface" />
-</selector>
\ No newline at end of file
+<wallpaper
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:settingsSliceUri="content://com.android.frameworks.coretests/slice"
+    android:supportsAmbientMode="true"/>
diff --git a/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java b/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java
index dcea5b2..c2d8f91 100644
--- a/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java
+++ b/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java
@@ -16,13 +16,23 @@
 
 package android.app;
 
+import static android.app.PropertyInvalidatedCache.NONCE_UNSET;
+import static android.app.PropertyInvalidatedCache.NonceStore.INVALID_NONCE_INDEX;
+import static com.android.internal.os.Flags.FLAG_APPLICATION_SHARED_MEMORY_ENABLED;
+
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertNotSame;
 import static org.junit.Assert.assertSame;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
+import com.android.internal.os.ApplicationSharedMemory;
+
 import android.platform.test.annotations.IgnoreUnderRavenwood;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
 import android.platform.test.ravenwood.RavenwoodRule;
 
 import androidx.test.filters.SmallTest;
@@ -42,10 +52,9 @@
  *  atest FrameworksCoreTests:PropertyInvalidatedCacheTests
  */
 @SmallTest
-@IgnoreUnderRavenwood(blockedBy = PropertyInvalidatedCache.class)
 public class PropertyInvalidatedCacheTests {
-    @Rule
-    public final RavenwoodRule mRavenwood = new RavenwoodRule();
+    public final CheckFlagsRule mCheckFlagsRule =
+            DeviceFlagsValueProvider.createCheckFlagsRule();
 
     // Configuration for creating caches
     private static final String MODULE = PropertyInvalidatedCache.MODULE_TEST;
@@ -376,9 +385,11 @@
         assertEquals(n1, "cache_key.bluetooth.get_state");
     }
 
-    // Verify that test mode works properly.
+    // Verify that invalidating the cache from an app process would fail due to lack of permissions.
     @Test
-    public void testTestMode() {
+    @android.platform.test.annotations.DisabledOnRavenwood(
+            reason = "SystemProperties doesn't have permission check")
+    public void testPermissionFailure() {
         // Create a cache that will write a system nonce.
         TestCache sysCache = new TestCache(PropertyInvalidatedCache.MODULE_SYSTEM, "mode1");
         try {
@@ -390,6 +401,13 @@
             // The expected exception is a bare RuntimeException.  The test does not attempt to
             // validate the text of the exception message.
         }
+    }
+
+    // Verify that test mode works properly.
+    @Test
+    public void testTestMode() {
+        // Create a cache that will write a system nonce.
+        TestCache sysCache = new TestCache(PropertyInvalidatedCache.MODULE_SYSTEM, "mode1");
 
         sysCache.testPropertyName();
         // Invalidate the cache.  This must succeed because the property has been marked for
@@ -407,7 +425,9 @@
         PropertyInvalidatedCache.setTestMode(false);
         try {
             PropertyInvalidatedCache.setTestMode(false);
-            fail("expected an IllegalStateException");
+            if (Flags.enforcePicTestmodeProtocol()) {
+                fail("expected an IllegalStateException");
+            }
         } catch (IllegalStateException e) {
             // The expected exception.
         }
@@ -423,4 +443,56 @@
         // Re-enable test mode (so that the cleanup for the test does not throw).
         PropertyInvalidatedCache.setTestMode(true);
     }
+
+    // Verify the behavior of shared memory nonce storage.  This does not directly test the cache
+    // storing nonces in shared memory.
+    @RequiresFlagsEnabled(FLAG_APPLICATION_SHARED_MEMORY_ENABLED)
+    @Test
+    @android.platform.test.annotations.DisabledOnRavenwood(
+            reason = "PIC doesn't use SharedMemory on Ravenwood")
+    public void testSharedMemoryStorage() {
+        // Fetch a shared memory instance for testing.
+        ApplicationSharedMemory shmem = ApplicationSharedMemory.create();
+
+        // Create a server-side store and a client-side store.  The server's store is mutable and
+        // the client's store is not mutable.
+        PropertyInvalidatedCache.NonceStore server =
+                new PropertyInvalidatedCache.NonceStore(shmem.getSystemNonceBlock(), true);
+        PropertyInvalidatedCache.NonceStore client =
+                new PropertyInvalidatedCache.NonceStore(shmem.getSystemNonceBlock(), false);
+
+        final String name1 = "name1";
+        assertEquals(server.getHandleForName(name1), INVALID_NONCE_INDEX);
+        assertEquals(client.getHandleForName(name1), INVALID_NONCE_INDEX);
+        final int index1 = server.storeName(name1);
+        assertNotEquals(index1, INVALID_NONCE_INDEX);
+        assertEquals(server.getHandleForName(name1), index1);
+        assertEquals(client.getHandleForName(name1), index1);
+        assertEquals(server.storeName(name1), index1);
+
+        assertEquals(server.getNonce(index1), NONCE_UNSET);
+        assertEquals(client.getNonce(index1), NONCE_UNSET);
+        final int value1 = 4;
+        server.setNonce(index1, value1);
+        assertEquals(server.getNonce(index1), value1);
+        assertEquals(client.getNonce(index1), value1);
+        final int value2 = 8;
+        server.setNonce(index1, value2);
+        assertEquals(server.getNonce(index1), value2);
+        assertEquals(client.getNonce(index1), value2);
+
+        final String name2 = "name2";
+        assertEquals(server.getHandleForName(name2), INVALID_NONCE_INDEX);
+        assertEquals(client.getHandleForName(name2), INVALID_NONCE_INDEX);
+        final int index2 = server.storeName(name2);
+        assertNotEquals(index2, INVALID_NONCE_INDEX);
+        assertEquals(server.getHandleForName(name2), index2);
+        assertEquals(client.getHandleForName(name2), index2);
+        assertEquals(server.storeName(name2), index2);
+
+        // The names are different, so the indices must be different.
+        assertNotEquals(index1, index2);
+
+        shmem.close();
+    }
 }
diff --git a/core/tests/coretests/src/android/app/wallpaper/OWNERS b/core/tests/coretests/src/android/app/wallpaper/OWNERS
new file mode 100644
index 0000000..93b068d
--- /dev/null
+++ b/core/tests/coretests/src/android/app/wallpaper/OWNERS
@@ -0,0 +1 @@
+include platform/frameworks/base:/core/java/android/service/wallpaper/OWNERS
diff --git a/core/tests/coretests/src/android/app/wallpaper/WallpaperDescriptionTest.java b/core/tests/coretests/src/android/app/wallpaper/WallpaperDescriptionTest.java
new file mode 100644
index 0000000..01c2abf
--- /dev/null
+++ b/core/tests/coretests/src/android/app/wallpaper/WallpaperDescriptionTest.java
@@ -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 android.app.wallpaper;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import android.content.ComponentName;
+import android.net.Uri;
+import android.os.Parcel;
+import android.os.PersistableBundle;
+import android.util.Xml;
+
+import com.android.modules.utils.TypedXmlPullParser;
+import com.android.modules.utils.TypedXmlSerializer;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.List;
+
+@RunWith(JUnit4.class)
+public class WallpaperDescriptionTest {
+    private static final String TAG = "WallpaperDescriptionTest";
+
+    private final ComponentName mTestComponent = new ComponentName("fakePackage", "fakeClass");
+
+    @Test
+    public void equals_ignoresIrrelevantFields() {
+        String id = "fakeId";
+        WallpaperDescription desc1 = new WallpaperDescription.Builder().setComponent(
+                mTestComponent).setId(id).setTitle("fake one").build();
+        WallpaperDescription desc2 = new WallpaperDescription.Builder().setComponent(
+                mTestComponent).setId(id).setTitle("fake different").build();
+
+        assertThat(desc1).isEqualTo(desc2);
+    }
+
+    @Test
+    public void hash_ignoresIrrelevantFields() {
+        String id = "fakeId";
+        WallpaperDescription desc1 = new WallpaperDescription.Builder().setComponent(
+                mTestComponent).setId(id).setTitle("fake one").build();
+        WallpaperDescription desc2 = new WallpaperDescription.Builder().setComponent(
+                mTestComponent).setId(id).setTitle("fake different").build();
+
+        assertThat(desc1.hashCode()).isEqualTo(desc2.hashCode());
+    }
+
+    @Test
+    public void xml_roundTripSucceeds() throws IOException, XmlPullParserException {
+        final Uri thumbnail = Uri.parse("http://www.bogus.com/thumbnail");
+        final List<CharSequence> description = List.of("line1", "line2");
+        final Uri contextUri = Uri.parse("http://www.bogus.com/contextUri");
+        final PersistableBundle content = new PersistableBundle();
+        content.putString("ckey", "cvalue");
+        WallpaperDescription source = new WallpaperDescription.Builder()
+                .setComponent(mTestComponent).setId("fakeId").setThumbnail(thumbnail)
+                .setTitle("Fake title").setDescription(description)
+                .setContextUri(contextUri).setContextDescription("Context description")
+                .setContent(content).build();
+
+        ByteArrayOutputStream ostream = new ByteArrayOutputStream();
+        TypedXmlSerializer serializer = Xml.newBinarySerializer();
+        serializer.setOutput(ostream, StandardCharsets.UTF_8.name());
+        serializer.startDocument(null, true);
+        serializer.startTag(null, "test");
+        source.saveToXml(serializer);
+        serializer.endTag(null, "test");
+        serializer.endDocument();
+        ostream.close();
+
+        WallpaperDescription destination = null;
+        ByteArrayInputStream istream = new ByteArrayInputStream(ostream.toByteArray());
+        TypedXmlPullParser parser = Xml.newBinaryPullParser();
+        parser.setInput(istream, StandardCharsets.UTF_8.name());
+        int type;
+        do {
+            type = parser.next();
+            if (type == XmlPullParser.START_TAG && "test".equals(parser.getName())) {
+                destination = WallpaperDescription.restoreFromXml(parser);
+            }
+        } while (type != XmlPullParser.END_DOCUMENT);
+
+        assertThat(destination).isNotNull();
+        assertThat(destination.getComponent()).isEqualTo(source.getComponent());
+        assertThat(destination.getId()).isEqualTo(source.getId());
+        assertThat(destination.getThumbnail()).isEqualTo(source.getThumbnail());
+        assertWithMessage("title mismatch").that(
+                CharSequence.compare(destination.getTitle(), source.getTitle())).isEqualTo(0);
+        assertThat(destination.getDescription()).hasSize(source.getDescription().size());
+        for (int i = 0; i < destination.getDescription().size(); i++) {
+            CharSequence strDest = destination.getDescription().get(i);
+            CharSequence strSrc = source.getDescription().get(i);
+            assertWithMessage("description string mismatch")
+                    .that(CharSequence.compare(strDest, strSrc)).isEqualTo(0);
+        }
+        assertThat(destination.getContextUri()).isEqualTo(source.getContextUri());
+        assertWithMessage("context description mismatch").that(
+                CharSequence.compare(destination.getContextDescription(),
+                source.getContextDescription())).isEqualTo(0);
+        assertThat(destination.getContent()).isNotNull();
+        assertThat(destination.getContent().getString("ckey")).isEqualTo(
+                source.getContent().getString("ckey"));
+    }
+
+    @Test
+    public void parcel_roundTripSucceeds() {
+        final Uri thumbnail = Uri.parse("http://www.bogus.com/thumbnail");
+        final List<CharSequence> description = List.of("line1", "line2");
+        final Uri contextUri = Uri.parse("http://www.bogus.com/contextUri");
+        final PersistableBundle content = new PersistableBundle();
+        content.putString("ckey", "cvalue");
+        WallpaperDescription source = new WallpaperDescription.Builder().setComponent(
+                mTestComponent).setId("fakeId").setThumbnail(thumbnail).setTitle(
+                "Fake title").setDescription(description).setContextUri(
+                contextUri).setContextDescription("Context description").setContent(
+                content).build();
+
+        Parcel parcel = Parcel.obtain();
+        source.writeToParcel(parcel, 0);
+        // Reset parcel for reading
+        parcel.setDataPosition(0);
+        WallpaperDescription destination = WallpaperDescription.CREATOR.createFromParcel(parcel);
+
+        assertThat(destination.getComponent()).isEqualTo(source.getComponent());
+        assertThat(destination.getId()).isEqualTo(source.getId());
+        assertThat(destination.getThumbnail()).isEqualTo(source.getThumbnail());
+        assertWithMessage("title mismatch").that(
+                CharSequence.compare(destination.getTitle(), source.getTitle())).isEqualTo(0);
+        assertThat(destination.getDescription()).hasSize(source.getDescription().size());
+        for (int i = 0; i < destination.getDescription().size(); i++) {
+            CharSequence strDest = destination.getDescription().get(i);
+            CharSequence strSrc = source.getDescription().get(i);
+            assertWithMessage("description string mismatch")
+                    .that(CharSequence.compare(strDest, strSrc)).isEqualTo(0);
+        }
+        assertThat(destination.getContextUri()).isEqualTo(source.getContextUri());
+        assertWithMessage("context description mismatch").that(
+                CharSequence.compare(destination.getContextDescription(),
+                        source.getContextDescription())).isEqualTo(0);
+        assertThat(destination.getContent()).isNotNull();
+        assertThat(destination.getContent().getString("ckey")).isEqualTo(
+                source.getContent().getString("ckey"));
+    }
+
+    @Test
+    public void parcel_roundTripSucceeds_withNulls() {
+        WallpaperDescription source = new WallpaperDescription.Builder().build();
+
+        Parcel parcel = Parcel.obtain();
+        source.writeToParcel(parcel, 0);
+        // Reset parcel for reading
+        parcel.setDataPosition(0);
+        WallpaperDescription destination = WallpaperDescription.CREATOR.createFromParcel(parcel);
+
+        assertThat(destination.getComponent()).isEqualTo(source.getComponent());
+        assertThat(destination.getId()).isEqualTo(source.getId());
+        assertThat(destination.getThumbnail()).isEqualTo(source.getThumbnail());
+        assertThat(destination.getTitle()).isNull();
+        assertThat(destination.getDescription()).hasSize(source.getDescription().size());
+        for (int i = 0; i < destination.getDescription().size(); i++) {
+            CharSequence strDest = destination.getDescription().get(i);
+            CharSequence strSrc = source.getDescription().get(i);
+            assertWithMessage("description string mismatch")
+                    .that(CharSequence.compare(strDest, strSrc)).isEqualTo(0);
+        }
+        assertThat(destination.getContextUri()).isEqualTo(source.getContextUri());
+        assertThat(destination.getContextDescription()).isNull();
+        assertThat(destination.getContent()).isNotNull();
+        assertThat(destination.getContent().keySet()).isEmpty();
+    }
+
+    @Test
+    public void toBuilder_succeeds() {
+        final String sourceId = "sourceId";
+        final Uri thumbnail = Uri.parse("http://www.bogus.com/thumbnail");
+        final List<CharSequence> description = List.of("line1", "line2");
+        final Uri contextUri = Uri.parse("http://www.bogus.com/contextUri");
+        final PersistableBundle content = new PersistableBundle();
+        content.putString("ckey", "cvalue");
+        final String destinationId = "destinationId";
+        WallpaperDescription source = new WallpaperDescription.Builder().setComponent(
+                mTestComponent).setId(sourceId).setThumbnail(thumbnail).setTitle(
+                "Fake title").setDescription(description).setContextUri(
+                contextUri).setContextDescription("Context description").setContent(
+                content).build();
+
+        WallpaperDescription destination = source.toBuilder().setId(destinationId).build();
+
+        assertThat(destination.getComponent()).isEqualTo(source.getComponent());
+        assertThat(destination.getId()).isEqualTo(destinationId);
+        assertThat(destination.getThumbnail()).isEqualTo(source.getThumbnail());
+        assertWithMessage("title mismatch").that(
+                CharSequence.compare(destination.getTitle(), source.getTitle())).isEqualTo(0);
+        assertThat(destination.getDescription()).hasSize(source.getDescription().size());
+        for (int i = 0; i < destination.getDescription().size(); i++) {
+            CharSequence strDest = destination.getDescription().get(i);
+            CharSequence strSrc = source.getDescription().get(i);
+            assertWithMessage("description string mismatch")
+                    .that(CharSequence.compare(strDest, strSrc)).isEqualTo(0);
+        }
+        assertThat(destination.getContextUri()).isEqualTo(source.getContextUri());
+        assertWithMessage("context description mismatch").that(
+                CharSequence.compare(destination.getContextDescription(),
+                        source.getContextDescription())).isEqualTo(0);
+        assertThat(destination.getContent()).isNotNull();
+        assertThat(destination.getContent().getString("ckey")).isEqualTo(
+                source.getContent().getString("ckey"));
+    }
+}
diff --git a/core/tests/coretests/src/android/app/wallpaper/WallpaperInstanceTest.java b/core/tests/coretests/src/android/app/wallpaper/WallpaperInstanceTest.java
new file mode 100644
index 0000000..d5a8937
--- /dev/null
+++ b/core/tests/coretests/src/android/app/wallpaper/WallpaperInstanceTest.java
@@ -0,0 +1,189 @@
+/*
+ * 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.app.wallpaper;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.app.WallpaperInfo;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.os.Parcel;
+import android.service.wallpaper.WallpaperService;
+
+import androidx.test.InstrumentationRegistry;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.util.List;
+
+@RunWith(JUnit4.class)
+public class WallpaperInstanceTest {
+    @Test
+    public void equals_bothNullInfo_sameId_isTrue() {
+        WallpaperDescription description = new WallpaperDescription.Builder().setId("123").build();
+        WallpaperInstance instance1 = new WallpaperInstance(null, description);
+        WallpaperInstance instance2 = new WallpaperInstance(null, description);
+
+        assertThat(instance1).isEqualTo(instance2);
+    }
+
+    @Test
+    public void equals_bothNullInfo_differentIds_isFalse() {
+        WallpaperDescription description1 = new WallpaperDescription.Builder().setId("123").build();
+        WallpaperDescription description2 = new WallpaperDescription.Builder().setId("456").build();
+        WallpaperInstance instance1 = new WallpaperInstance(null, description1);
+        WallpaperInstance instance2 = new WallpaperInstance(null, description2);
+
+        assertThat(instance1).isNotEqualTo(instance2);
+    }
+
+    @Test
+    public void equals_singleNullInfo_isFalse() throws Exception {
+        WallpaperDescription description = new WallpaperDescription.Builder().build();
+        WallpaperInstance instance1 = new WallpaperInstance(null, description);
+        WallpaperInstance instance2 = new WallpaperInstance(makeWallpaperInfo(), description);
+
+        assertThat(instance1).isNotEqualTo(instance2);
+    }
+
+    @Test
+    public void equals_sameInfoAndId_isTrue() throws Exception {
+        WallpaperDescription description = new WallpaperDescription.Builder().setId("123").build();
+        WallpaperInstance instance1 = new WallpaperInstance(makeWallpaperInfo(), description);
+        WallpaperInstance instance2 = new WallpaperInstance(makeWallpaperInfo(), description);
+
+        assertThat(instance1).isEqualTo(instance2);
+    }
+
+    @Test
+    public void equals_sameInfo_differentIds_isFalse() throws Exception {
+        WallpaperDescription description1 = new WallpaperDescription.Builder().setId("123").build();
+        WallpaperDescription description2 = new WallpaperDescription.Builder().setId("456").build();
+        WallpaperInstance instance1 = new WallpaperInstance(makeWallpaperInfo(), description1);
+        WallpaperInstance instance2 = new WallpaperInstance(makeWallpaperInfo(), description2);
+
+        assertThat(instance1).isNotEqualTo(instance2);
+    }
+
+    @Test
+    public void hash_nullInfo_works() {
+        WallpaperDescription description1 = new WallpaperDescription.Builder().setId("123").build();
+        WallpaperDescription description2 = new WallpaperDescription.Builder().setId("456").build();
+        WallpaperInstance base = new WallpaperInstance(null, description1);
+        WallpaperInstance sameId = new WallpaperInstance(null, description1);
+        WallpaperInstance differentId = new WallpaperInstance(null, description2);
+
+        assertThat(base.hashCode()).isEqualTo(sameId.hashCode());
+        assertThat(base.hashCode()).isNotEqualTo(differentId.hashCode());
+    }
+
+    @Test
+    public void hash_withInfo_works() throws Exception {
+        WallpaperDescription description1 = new WallpaperDescription.Builder().setId("123").build();
+        WallpaperDescription description2 = new WallpaperDescription.Builder().setId("456").build();
+        WallpaperInstance base = new WallpaperInstance(makeWallpaperInfo(), description1);
+        WallpaperInstance sameId = new WallpaperInstance(makeWallpaperInfo(), description1);
+        WallpaperInstance differentId = new WallpaperInstance(makeWallpaperInfo(), description2);
+
+        assertThat(base.hashCode()).isEqualTo(sameId.hashCode());
+        assertThat(base.hashCode()).isNotEqualTo(differentId.hashCode());
+    }
+
+    @Test
+    public void id_fromOverride() throws Exception {
+        final String id = "override";
+        WallpaperInstance instance = new WallpaperInstance(makeWallpaperInfo(),
+                new WallpaperDescription.Builder().setId("abc123").build(), id);
+
+        assertThat(instance.getId()).isEqualTo(id);
+    }
+
+    @Test
+    public void id_fromDescription() throws Exception {
+        final String id = "abc123";
+        WallpaperInstance instance = new WallpaperInstance(makeWallpaperInfo(),
+                new WallpaperDescription.Builder().setId(id).build());
+
+        assertThat(instance.getId()).isEqualTo(id);
+    }
+
+    @Test
+    public void id_fromComponent() throws Exception {
+        WallpaperInfo info = makeWallpaperInfo();
+        WallpaperInstance instance = new WallpaperInstance(info,
+                new WallpaperDescription.Builder().build());
+
+        assertThat(instance.getId()).isEqualTo(info.getComponent().flattenToString());
+    }
+
+    @Test
+    public void id_default() {
+        WallpaperInstance instance = new WallpaperInstance(null,
+                new WallpaperDescription.Builder().build());
+
+        assertThat(instance.getId()).isNotNull();
+    }
+
+    @Test
+    public void parcel_roundTripSucceeds() throws Exception {
+        WallpaperInstance source = new WallpaperInstance(makeWallpaperInfo(),
+                new WallpaperDescription.Builder().build());
+
+        Parcel parcel = Parcel.obtain();
+        source.writeToParcel(parcel, 0);
+        // Reset parcel for reading
+        parcel.setDataPosition(0);
+
+        WallpaperInstance destination = WallpaperInstance.CREATOR.createFromParcel(parcel);
+
+        assertThat(destination.getInfo()).isNotNull();
+        assertThat(destination.getInfo().getComponent()).isEqualTo(source.getInfo().getComponent());
+        assertThat(destination.getId()).isEqualTo(source.getId());
+        assertThat(destination.getDescription()).isEqualTo(source.getDescription());
+    }
+
+    @Test
+    public void parcel_roundTripSucceeds_withNulls() {
+        WallpaperInstance source = new WallpaperInstance(null,
+                new WallpaperDescription.Builder().build());
+
+        Parcel parcel = Parcel.obtain();
+        source.writeToParcel(parcel, 0);
+        // Reset parcel for reading
+        parcel.setDataPosition(0);
+
+        WallpaperInstance destination = WallpaperInstance.CREATOR.createFromParcel(parcel);
+
+        assertThat(destination.getInfo()).isEqualTo(source.getInfo());
+        assertThat(destination.getId()).isEqualTo(source.getId());
+        assertThat(destination.getDescription()).isEqualTo(source.getDescription());
+    }
+
+    private WallpaperInfo makeWallpaperInfo() throws Exception {
+        Context context = InstrumentationRegistry.getTargetContext();
+        Intent intent = new Intent(WallpaperService.SERVICE_INTERFACE);
+        intent.setPackage("com.android.frameworks.coretests");
+        PackageManager pm = context.getPackageManager();
+        List<ResolveInfo> result = pm.queryIntentServices(intent, PackageManager.GET_META_DATA);
+        assertThat(result).hasSize(1);
+        ResolveInfo info = result.getFirst();
+        return new WallpaperInfo(context, info);
+    }
+}
diff --git a/core/tests/coretests/src/android/content/pm/parsing/ApkLiteParseUtilsTest.java b/core/tests/coretests/src/android/content/pm/parsing/ApkLiteParseUtilsTest.java
new file mode 100644
index 0000000..d4618d7
--- /dev/null
+++ b/core/tests/coretests/src/android/content/pm/parsing/ApkLiteParseUtilsTest.java
@@ -0,0 +1,326 @@
+/*
+ * 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.content.pm.parsing;
+
+import static android.content.pm.PackageManager.GET_SIGNING_CERTIFICATES;
+import static android.content.pm.PackageManager.MATCH_STATIC_SHARED_AND_SDK_LIBRARIES;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.annotation.SuppressLint;
+import android.app.UiAutomation;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.Signature;
+import android.content.pm.SigningInfo;
+import android.content.pm.parsing.result.ParseResult;
+import android.content.pm.parsing.result.ParseTypeImpl;
+import android.os.FileUtils;
+import android.os.ParcelFileDescriptor;
+import android.platform.test.annotations.Presubmit;
+import android.util.ArraySet;
+import android.util.PackageUtils;
+
+import androidx.annotation.NonNull;
+import androidx.test.InstrumentationRegistry;
+
+import com.android.internal.pm.parsing.PackageParser2;
+import com.android.server.pm.pkg.AndroidPackage;
+
+import libcore.util.HexEncoding;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Set;
+
+@Presubmit
+public class ApkLiteParseUtilsTest {
+
+    @Rule
+    public TemporaryFolder mTemporaryFolder = new TemporaryFolder();
+
+    private static final String PUSH_FILE_DIR = "/data/local/tmp/tests/coretests/pm/";
+    private static final String TEST_APP_USING_SDK1_AND_SDK2 = "HelloWorldUsingSdk1And2.apk";
+    private static final String TEST_APP_USING_SDK1_AND_SDK1 = "HelloWorldUsingSdk1AndSdk1.apk";
+    private static final String TEST_APP_USING_SDK_MALFORMED_VERSION =
+            "HelloWorldUsingSdkMalformedNegativeVersion.apk";
+    private static final String TEST_APP_USING_STATIC_LIB = "CtsStaticSharedLibConsumerApp1.apk";
+    private static final String TEST_SDK1 = "HelloWorldSdk1.apk";
+    private static final String TEST_SDK1_PACKAGE = "com.test.sdk1_1";
+    private static final String TEST_SDK1_NAME = "com.test.sdk1";
+    private static final long TEST_SDK1_VERSION = 1;
+    private static final String TEST_SDK2_NAME = "com.test.sdk2";
+    private static final long TEST_SDK2_VERSION = 2;
+
+    private final PackageParser2 mPackageParser2 = new PackageParser2(
+            null, null, null, new FakePackageParser2Callback());
+
+    private File mTmpDir = null;
+
+    @Before
+    public void setUp() throws IOException {
+        mTmpDir = mTemporaryFolder.newFolder("DexMetadataHelperTest");
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        setSystemProperty("debug.pm.uses_sdk_library_default_cert_digest", "invalid");
+        uninstallPackageSilently(TEST_SDK1_NAME);
+    }
+
+    @SuppressLint("CheckResult")
+    @Test
+    public void testParseApkLite_getUsesSdkLibrary() throws Exception {
+        File apkFile = copyApkToTmpDir(TEST_APP_USING_SDK1_AND_SDK2);
+        ParseResult<ApkLite> result = ApkLiteParseUtils
+                .parseApkLite(ParseTypeImpl.forDefaultParsing().reset(), apkFile, 0);
+        assertThat(result.isError()).isFalse();
+
+        ApkLite baseApk = result.getResult();
+        assertThat(baseApk.getUsesSdkLibraries()).containsExactly(TEST_SDK1_NAME, TEST_SDK2_NAME);
+        assertThat(baseApk.getUsesSdkLibrariesVersionsMajor()).asList().containsExactly(
+                TEST_SDK1_VERSION, TEST_SDK2_VERSION
+        );
+        for (String[] certDigests: baseApk.getUsesSdkLibrariesCertDigests()) {
+            assertThat(certDigests).asList().containsExactly("");
+        }
+    }
+
+    @SuppressLint("CheckResult")
+    @Test
+    public void testParseApkLite_getUsesSdkLibrary_overrideCertDigest() throws Exception {
+        installPackage(TEST_SDK1);
+        String certDigest = getPackageCertDigest(TEST_SDK1_PACKAGE);
+        overrideUsesSdkLibraryCertificateDigest(certDigest);
+
+        File apkFile = copyApkToTmpDir(TEST_APP_USING_SDK1_AND_SDK2);
+        ParseResult<ApkLite> result = ApkLiteParseUtils
+                .parseApkLite(ParseTypeImpl.forDefaultParsing().reset(), apkFile, 0);
+        ApkLite baseApk = result.getResult();
+
+        String[][] liteCerts = baseApk.getUsesSdkLibrariesCertDigests();
+        assertThat(liteCerts).isNotNull();
+        for (String[] certDigests: liteCerts) {
+            assertThat(certDigests).asList().containsExactly(certDigest);
+        }
+
+        // Same for package parser
+        AndroidPackage pkg = mPackageParser2.parsePackage(apkFile, 0, true).hideAsFinal();
+        String[][] pkgCerts = pkg.getUsesSdkLibrariesCertDigests();
+        assertThat(pkgCerts).isNotNull();
+        for (int i = 0; i < liteCerts.length; i++) {
+            assertThat(liteCerts[i]).isEqualTo(pkgCerts[i]);
+        }
+    }
+
+
+    @SuppressLint("CheckResult")
+    @Test
+    public void testParseApkLite_getUsesSdkLibrary_sameAsPackageParser() throws Exception {
+        File apkFile = copyApkToTmpDir(TEST_APP_USING_SDK1_AND_SDK2);
+        ParseResult<ApkLite> result = ApkLiteParseUtils
+                .parseApkLite(ParseTypeImpl.forDefaultParsing().reset(), apkFile, 0);
+        assertThat(result.isError()).isFalse();
+        ApkLite baseApk = result.getResult();
+
+        AndroidPackage pkg = mPackageParser2.parsePackage(apkFile, 0, true).hideAsFinal();
+        assertThat(baseApk.getUsesSdkLibraries())
+                .containsExactlyElementsIn(pkg.getUsesSdkLibraries());
+        List<Long> versionsBoxed = Arrays.stream(pkg.getUsesSdkLibrariesVersionsMajor()).boxed()
+                .toList();
+        assertThat(baseApk.getUsesSdkLibrariesVersionsMajor()).asList()
+                .containsExactlyElementsIn(versionsBoxed);
+
+        String[][] liteCerts = baseApk.getUsesSdkLibrariesCertDigests();
+        String[][] pkgCerts = pkg.getUsesSdkLibrariesCertDigests();
+        for (int i = 0; i < liteCerts.length; i++) {
+            assertThat(liteCerts[i]).isEqualTo(pkgCerts[i]);
+        }
+    }
+
+    @SuppressLint("CheckResult")
+    @Test
+    public void testParseApkLite_getUsesStaticLibrary_sameAsPackageParser() throws Exception {
+        File apkFile = copyApkToTmpDir(TEST_APP_USING_STATIC_LIB);
+        ParseResult<ApkLite> result = ApkLiteParseUtils
+                .parseApkLite(ParseTypeImpl.forDefaultParsing().reset(), apkFile, 0);
+        assertThat(result.isError()).isFalse();
+        ApkLite baseApk = result.getResult();
+
+        AndroidPackage pkg = mPackageParser2.parsePackage(apkFile, 0, true).hideAsFinal();
+        assertThat(baseApk.getUsesStaticLibraries())
+                .containsExactlyElementsIn(pkg.getUsesStaticLibraries());
+        List<Long> versionsBoxed = Arrays.stream(pkg.getUsesStaticLibrariesVersions()).boxed()
+                .toList();
+        assertThat(baseApk.getUsesStaticLibrariesVersions()).asList()
+                .containsExactlyElementsIn(versionsBoxed);
+
+        String[][] liteCerts = baseApk.getUsesStaticLibrariesCertDigests();
+        String[][] pkgCerts = pkg.getUsesStaticLibrariesCertDigests();
+        for (int i = 0; i < liteCerts.length; i++) {
+            assertThat(liteCerts[i]).isEqualTo(pkgCerts[i]);
+        }
+    }
+
+    @SuppressLint("CheckResult")
+    @Test
+    public void testParseApkLite_malformedUsesSdkLibrary_duplicate() throws Exception {
+        File apkFile = copyApkToTmpDir(TEST_APP_USING_SDK1_AND_SDK1);
+        ParseResult<ApkLite> result = ApkLiteParseUtils
+                .parseApkLite(ParseTypeImpl.forDefaultParsing().reset(), apkFile, 0);
+        assertThat(result.isError()).isTrue();
+        assertThat(result.getErrorMessage()).contains("Bad uses-sdk-library declaration");
+        assertThat(result.getErrorMessage()).contains(
+                "Depending on multiple versions of SDK library");
+    }
+
+    @SuppressLint("CheckResult")
+    @Test
+    public void testParseApkLite_malformedUsesSdkLibrary_missingVersion() throws Exception {
+        File apkFile = copyApkToTmpDir(TEST_APP_USING_SDK_MALFORMED_VERSION);
+        ParseResult<ApkLite> result = ApkLiteParseUtils
+                .parseApkLite(ParseTypeImpl.forDefaultParsing().reset(), apkFile, 0);
+        assertThat(result.isError()).isTrue();
+        assertThat(result.getErrorMessage()).contains("Bad uses-sdk-library declaration");
+    }
+
+    private String getPackageCertDigest(String packageName) throws Exception {
+        getUiAutomation().adoptShellPermissionIdentity();
+        try {
+            PackageInfo sdkPackageInfo = getPackageManager().getPackageInfo(packageName,
+                    PackageManager.PackageInfoFlags.of(
+                            GET_SIGNING_CERTIFICATES | MATCH_STATIC_SHARED_AND_SDK_LIBRARIES));
+            SigningInfo signingInfo = sdkPackageInfo.signingInfo;
+            Signature[] signatures =
+                    signingInfo != null ? signingInfo.getSigningCertificateHistory() : null;
+            byte[] digest = PackageUtils.computeSha256DigestBytes(signatures[0].toByteArray());
+            return new String(HexEncoding.encode(digest));
+        } finally {
+            getUiAutomation().dropShellPermissionIdentity();
+        }
+    }
+
+    private static PackageManager getPackageManager() {
+        return InstrumentationRegistry.getContext().getPackageManager();
+    }
+
+    /**
+     * SDK package is signed by build system. In theory we could try to extract the signature,
+     * and patch the app manifest. This property allows us to override in runtime, which is much
+     * easier.
+     */
+    private void overrideUsesSdkLibraryCertificateDigest(String sdkCertDigest) throws Exception {
+        setSystemProperty("debug.pm.uses_sdk_library_default_cert_digest", sdkCertDigest);
+    }
+
+    private void setSystemProperty(String name, String value) throws Exception {
+        assertThat(executeShellCommand("setprop " + name + " " + value)).isEmpty();
+    }
+
+    private static String executeShellCommand(String command) throws IOException {
+        final ParcelFileDescriptor stdout = getUiAutomation().executeShellCommand(command);
+        try (InputStream inputStream = new ParcelFileDescriptor.AutoCloseInputStream(stdout)) {
+            return readFullStream(inputStream);
+        }
+    }
+
+    private static String readFullStream(InputStream inputStream) throws IOException {
+        ByteArrayOutputStream result = new ByteArrayOutputStream();
+        writeFullStream(inputStream, result, -1);
+        return result.toString("UTF-8");
+    }
+
+    private static void writeFullStream(InputStream inputStream, OutputStream outputStream,
+            long expected) throws IOException {
+        byte[] buffer = new byte[1024];
+        long total = 0;
+        int length;
+        while ((length = inputStream.read(buffer)) != -1) {
+            outputStream.write(buffer, 0, length);
+            total += length;
+        }
+        if (expected > 0) {
+            assertThat(expected).isEqualTo(total);
+        }
+    }
+
+    private static UiAutomation getUiAutomation() {
+        return InstrumentationRegistry.getInstrumentation().getUiAutomation();
+    }
+
+    private File copyApkToTmpDir(String apkFileName) throws Exception {
+        File outFile = new File(mTmpDir, apkFileName);
+        String apkFilePath = PUSH_FILE_DIR + apkFileName;
+        File apkFile = new File(apkFilePath);
+        assertThat(apkFile.exists()).isTrue();
+        try (InputStream is = new FileInputStream(apkFile)) {
+            FileUtils.copyToFileOrThrow(is, outFile);
+        }
+        return outFile;
+    }
+
+    static String createApkPath(String baseName) {
+        return PUSH_FILE_DIR + baseName;
+    }
+
+    /* Install for all the users */
+    private void installPackage(String baseName) throws IOException {
+        File file = new File(createApkPath(baseName));
+        assertThat(executeShellCommand("pm install -t -g " + file.getPath()))
+                .isEqualTo("Success\n");
+    }
+
+    private static String uninstallPackageSilently(String packageName) throws IOException {
+        return executeShellCommand("pm uninstall " + packageName);
+    }
+
+    static class FakePackageParser2Callback extends PackageParser2.Callback {
+
+        @Override
+        public boolean isChangeEnabled(long changeId, @NonNull ApplicationInfo appInfo) {
+            return true;
+        }
+
+        @Override
+        public boolean hasFeature(String feature) {
+            return true;
+        }
+
+        @Override
+        public @NonNull Set<String> getHiddenApiWhitelistedApps() {
+            return new ArraySet<>();
+        }
+
+        @Override
+        public @NonNull Set<String> getInstallConstraintsAllowlist() {
+            return new ArraySet<>();
+        }
+    }
+}
diff --git a/core/tests/coretests/src/android/content/pm/verify/VerificationSessionTest.java b/core/tests/coretests/src/android/content/pm/verify/VerificationSessionTest.java
index 90ae306..f1e1df5 100644
--- a/core/tests/coretests/src/android/content/pm/verify/VerificationSessionTest.java
+++ b/core/tests/coretests/src/android/content/pm/verify/VerificationSessionTest.java
@@ -142,10 +142,10 @@
                 new VerificationStatus.Builder().setVerified(true).build();
         mTestSession.reportVerificationComplete(status);
         verify(mTestSessionInterface, times(1)).reportVerificationComplete(
-                eq(TEST_ID), eq(status));
+                eq(TEST_ID), eq(status), eq(null));
         mTestSession.reportVerificationComplete(status, response);
         verify(mTestSessionInterface, times(1))
-                .reportVerificationCompleteWithExtensionResponse(
+                .reportVerificationComplete(
                         eq(TEST_ID), eq(status), eq(response));
 
         final int reason = VerificationSession.VERIFICATION_INCOMPLETE_UNKNOWN;
diff --git a/core/tests/coretests/src/android/os/IpcDataCacheTest.java b/core/tests/coretests/src/android/os/IpcDataCacheTest.java
index 5c56fdc..e14608a 100644
--- a/core/tests/coretests/src/android/os/IpcDataCacheTest.java
+++ b/core/tests/coretests/src/android/os/IpcDataCacheTest.java
@@ -41,10 +41,7 @@
  *  atest FrameworksCoreTests:IpcDataCacheTest
  */
 @SmallTest
-@IgnoreUnderRavenwood(blockedBy = IpcDataCache.class)
 public class IpcDataCacheTest {
-    @Rule
-    public final RavenwoodRule mRavenwood = new RavenwoodRule();
 
     // Configuration for creating caches
     private static final String MODULE = IpcDataCache.MODULE_TEST;
@@ -452,9 +449,11 @@
         assertEquals(ec.isDisabled(), true);
     }
 
-    // Verify that test mode works properly.
+    // Verify that invalidating the cache from an app process would fail due to lack of permissions.
     @Test
-    public void testTestMode() {
+    @android.platform.test.annotations.DisabledOnRavenwood(
+            reason = "SystemProperties doesn't have permission check")
+    public void testPermissionFailure() {
         // Create a cache that will write a system nonce.
         TestCache sysCache = new TestCache(IpcDataCache.MODULE_SYSTEM, "mode1");
         try {
@@ -466,6 +465,13 @@
             // The expected exception is a bare RuntimeException.  The test does not attempt to
             // validate the text of the exception message.
         }
+    }
+
+    // Verify that test mode works properly.
+    @Test
+    public void testTestMode() {
+        // Create a cache that will write a system nonce.
+        TestCache sysCache = new TestCache(IpcDataCache.MODULE_SYSTEM, "mode1");
 
         sysCache.testPropertyName();
         // Invalidate the cache.  This must succeed because the property has been marked for
@@ -483,7 +489,9 @@
         IpcDataCache.setTestMode(false);
         try {
             IpcDataCache.setTestMode(false);
-            fail("expected an IllegalStateException");
+            if (android.app.Flags.enforcePicTestmodeProtocol()) {
+                fail("expected an IllegalStateException");
+            }
         } catch (IllegalStateException e) {
             // The expected exception.
         }
diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java
index da202b6..930e03d 100644
--- a/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java
+++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java
@@ -46,7 +46,7 @@
     // The number of fields tested in the corresponding CTS AccessibilityNodeInfoTest:
     // See fullyPopulateAccessibilityNodeInfo, assertEqualsAccessibilityNodeInfo,
     // and assertAccessibilityNodeInfoCleared in that class.
-    private static final int NUM_MARSHALLED_PROPERTIES = 45;
+    private static final int NUM_MARSHALLED_PROPERTIES = 46;
 
     /**
      * The number of properties that are purposely not marshalled
diff --git a/core/tests/coretests/src/com/android/internal/jank/FrameTrackerTest.java b/core/tests/coretests/src/com/android/internal/jank/FrameTrackerTest.java
index 1cbc7d6..60b5a42 100644
--- a/core/tests/coretests/src/com/android/internal/jank/FrameTrackerTest.java
+++ b/core/tests/coretests/src/com/android/internal/jank/FrameTrackerTest.java
@@ -16,9 +16,9 @@
 
 package com.android.internal.jank;
 
-import static android.view.SurfaceControl.JankData.JANK_APP_DEADLINE_MISSED;
+import static android.view.SurfaceControl.JankData.JANK_APPLICATION;
+import static android.view.SurfaceControl.JankData.JANK_COMPOSER;
 import static android.view.SurfaceControl.JankData.JANK_NONE;
-import static android.view.SurfaceControl.JankData.JANK_SURFACEFLINGER_DEADLINE_MISSED;
 
 import static com.android.internal.jank.FrameTracker.SurfaceControlWrapper;
 import static com.android.internal.jank.FrameTracker.ViewRootWrapper;
@@ -164,7 +164,7 @@
         verify(mRenderer, only()).addObserver(any());
 
         // send first frame with a long duration - should not be taken into account
-        sendFirstWindowFrame(tracker, 100, JANK_APP_DEADLINE_MISSED, 100L);
+        sendFirstWindowFrame(tracker, 100, JANK_APPLICATION, 100L);
 
         // send another frame with a short duration - should not be considered janky
         sendFrame(tracker, 5, JANK_NONE, 101L);
@@ -173,7 +173,7 @@
         when(mChoreographer.getVsyncId()).thenReturn(102L);
         tracker.end(FrameTracker.REASON_END_NORMAL);
         sendFrame(tracker, 5, JANK_NONE, 102L);
-        sendFrame(tracker, 500, JANK_APP_DEADLINE_MISSED, 103L);
+        sendFrame(tracker, 500, JANK_APPLICATION, 103L);
 
         verify(tracker).removeObservers();
         verify(mTrackerListener, never()).triggerPerfetto(any());
@@ -202,7 +202,7 @@
         sendFrame(tracker, 4, JANK_NONE, 100L);
 
         // send another frame - should be considered janky
-        sendFrame(tracker, 40, JANK_SURFACEFLINGER_DEADLINE_MISSED, 101L);
+        sendFrame(tracker, 40, JANK_COMPOSER, 101L);
 
         // end the trace session
         when(mChoreographer.getVsyncId()).thenReturn(102L);
@@ -236,7 +236,7 @@
         verify(mRenderer, only()).addObserver(any());
 
         // send first frame - janky
-        sendFrame(tracker, 40, JANK_APP_DEADLINE_MISSED, 100L);
+        sendFrame(tracker, 40, JANK_APPLICATION, 100L);
 
         // send another frame - not jank
         sendFrame(tracker, 4, JANK_NONE, 101L);
@@ -275,7 +275,7 @@
         sendFrame(tracker, 4, JANK_NONE, 100L);
 
         // send another frame - should be considered janky
-        sendFrame(tracker, 40, JANK_APP_DEADLINE_MISSED, 101L);
+        sendFrame(tracker, 40, JANK_APPLICATION, 101L);
 
         // end the trace session
         when(mChoreographer.getVsyncId()).thenReturn(102L);
@@ -317,7 +317,7 @@
         // end the trace session, simulate one more valid callback came after the end call.
         when(mChoreographer.getVsyncId()).thenReturn(102L);
         tracker.end(FrameTracker.REASON_END_NORMAL);
-        sendFrame(tracker, 50, JANK_APP_DEADLINE_MISSED, 102L);
+        sendFrame(tracker, 50, JANK_APPLICATION, 102L);
 
         // One more callback with VSYNC after the end() vsync id.
         sendFrame(tracker, 4, JANK_NONE, 103L);
@@ -365,7 +365,7 @@
         sendSfFrame(tracker, 4, 102L, JANK_NONE);
 
         // Send janky but complete callbck fo 103L
-        sendFrame(tracker, 50, JANK_APP_DEADLINE_MISSED, 103L);
+        sendFrame(tracker, 50, JANK_APPLICATION, 103L);
 
         verify(tracker).removeObservers();
         verify(mTrackerListener, never()).triggerPerfetto(any());
@@ -397,7 +397,7 @@
         sendFrame(tracker, 4, JANK_NONE, 101L);
 
         // a janky frame
-        sendFrame(tracker, 50, JANK_APP_DEADLINE_MISSED, 102L);
+        sendFrame(tracker, 50, JANK_APPLICATION, 102L);
 
         tracker.cancel(FrameTracker.REASON_CANCEL_NORMAL);
         verify(tracker).removeObservers();
@@ -481,7 +481,7 @@
         // normal frame - not janky
         sendFrame(tracker, JANK_NONE, 101L);
         // a janky frame
-        sendFrame(tracker, JANK_APP_DEADLINE_MISSED, 102L);
+        sendFrame(tracker, JANK_APPLICATION, 102L);
 
         when(mChoreographer.getVsyncId()).thenReturn(102L);
         tracker.end(FrameTracker.REASON_CANCEL_NORMAL);
@@ -514,7 +514,7 @@
         verify(mSurfaceControlWrapper).addJankStatsListener(any(), any());
 
         // First frame - janky
-        sendFrame(tracker, JANK_APP_DEADLINE_MISSED, 100L);
+        sendFrame(tracker, JANK_APPLICATION, 100L);
         // normal frame - not janky
         sendFrame(tracker, JANK_NONE, 101L);
         // normal frame - not janky
@@ -561,7 +561,7 @@
         tracker.end(FrameTracker.REASON_CANCEL_NORMAL);
 
         // janky frame, should be ignored, trigger finish
-        sendFrame(tracker, JANK_APP_DEADLINE_MISSED, 103L);
+        sendFrame(tracker, JANK_APPLICATION, 103L);
 
         verify(mJankStatsRegistration).removeAfter(anyLong());
         verify(mTrackerListener, never()).triggerPerfetto(any());
@@ -623,16 +623,16 @@
         tracker.begin();
         mRunnableArgumentCaptor.getValue().run();
         verify(mSurfaceControlWrapper).addJankStatsListener(any(), any());
-        sendFrame(tracker, JANK_SURFACEFLINGER_DEADLINE_MISSED, 100L);
-        sendFrame(tracker, JANK_SURFACEFLINGER_DEADLINE_MISSED, 101L);
-        sendFrame(tracker, JANK_APP_DEADLINE_MISSED, 102L);
+        sendFrame(tracker, JANK_COMPOSER, 100L);
+        sendFrame(tracker, JANK_COMPOSER, 101L);
+        sendFrame(tracker, JANK_APPLICATION, 102L);
         sendFrame(tracker, JANK_NONE, 103L);
-        sendFrame(tracker, JANK_APP_DEADLINE_MISSED, 104L);
-        sendFrame(tracker, JANK_APP_DEADLINE_MISSED, 105L);
+        sendFrame(tracker, JANK_APPLICATION, 104L);
+        sendFrame(tracker, JANK_APPLICATION, 105L);
         when(mChoreographer.getVsyncId()).thenReturn(106L);
         tracker.end(FrameTracker.REASON_END_NORMAL);
-        sendFrame(tracker, JANK_SURFACEFLINGER_DEADLINE_MISSED, 106L);
-        sendFrame(tracker, JANK_SURFACEFLINGER_DEADLINE_MISSED, 107L);
+        sendFrame(tracker, JANK_COMPOSER, 106L);
+        sendFrame(tracker, JANK_COMPOSER, 107L);
         verify(mJankStatsRegistration).removeAfter(anyLong());
         verify(mTrackerListener).triggerPerfetto(any());
         verify(mStatsLog).write(eq(UI_INTERACTION_FRAME_INFO_REPORTED),
diff --git a/core/tests/coretests/src/com/android/internal/widget/NotificationProgressBarTest.java b/core/tests/coretests/src/com/android/internal/widget/NotificationProgressBarTest.java
index 79a478a..5613caf 100644
--- a/core/tests/coretests/src/com/android/internal/widget/NotificationProgressBarTest.java
+++ b/core/tests/coretests/src/com/android/internal/widget/NotificationProgressBarTest.java
@@ -41,9 +41,26 @@
         List<ProgressStyle.Segment> segments = new ArrayList<>();
         List<ProgressStyle.Point> points = new ArrayList<>();
         int progress = 50;
+        int progressMax = 100;
         boolean isStyledByProgress = true;
 
         NotificationProgressBar.processAndConvertToDrawableParts(segments, points, progress,
+                progressMax,
+                isStyledByProgress);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void processAndConvertToDrawableParts_segmentsLengthNotMatchingProgressMax() {
+        List<ProgressStyle.Segment> segments = new ArrayList<>();
+        segments.add(new ProgressStyle.Segment(50));
+        segments.add(new ProgressStyle.Segment(100));
+        List<ProgressStyle.Point> points = new ArrayList<>();
+        int progress = 50;
+        int progressMax = 100;
+        boolean isStyledByProgress = true;
+
+        NotificationProgressBar.processAndConvertToDrawableParts(segments, points, progress,
+                progressMax,
                 isStyledByProgress);
     }
 
@@ -51,11 +68,14 @@
     public void processAndConvertToDrawableParts_segmentLengthIsNegative() {
         List<ProgressStyle.Segment> segments = new ArrayList<>();
         segments.add(new ProgressStyle.Segment(-50));
+        segments.add(new ProgressStyle.Segment(150));
         List<ProgressStyle.Point> points = new ArrayList<>();
         int progress = 50;
+        int progressMax = 100;
         boolean isStyledByProgress = true;
 
         NotificationProgressBar.processAndConvertToDrawableParts(segments, points, progress,
+                progressMax,
                 isStyledByProgress);
     }
 
@@ -63,11 +83,14 @@
     public void processAndConvertToDrawableParts_segmentLengthIsZero() {
         List<ProgressStyle.Segment> segments = new ArrayList<>();
         segments.add(new ProgressStyle.Segment(0));
+        segments.add(new ProgressStyle.Segment(100));
         List<ProgressStyle.Point> points = new ArrayList<>();
         int progress = 50;
+        int progressMax = 100;
         boolean isStyledByProgress = true;
 
         NotificationProgressBar.processAndConvertToDrawableParts(segments, points, progress,
+                progressMax,
                 isStyledByProgress);
     }
 
@@ -77,9 +100,11 @@
         segments.add(new ProgressStyle.Segment(100));
         List<ProgressStyle.Point> points = new ArrayList<>();
         int progress = -50;
+        int progressMax = 100;
         boolean isStyledByProgress = true;
 
         NotificationProgressBar.processAndConvertToDrawableParts(segments, points, progress,
+                progressMax,
                 isStyledByProgress);
     }
 
@@ -89,10 +114,11 @@
         segments.add(new ProgressStyle.Segment(100).setColor(Color.RED));
         List<ProgressStyle.Point> points = new ArrayList<>();
         int progress = 0;
+        int progressMax = 100;
         boolean isStyledByProgress = true;
 
         List<Part> parts = NotificationProgressBar.processAndConvertToDrawableParts(
-                segments, points, progress, isStyledByProgress);
+                segments, points, progress, progressMax, isStyledByProgress);
 
         int fadedRed = 0x7FFF0000;
         List<Part> expected = new ArrayList<>(List.of(new Segment(1f, fadedRed, true)));
@@ -106,10 +132,11 @@
         segments.add(new ProgressStyle.Segment(100).setColor(Color.RED));
         List<ProgressStyle.Point> points = new ArrayList<>();
         int progress = 100;
+        int progressMax = 100;
         boolean isStyledByProgress = true;
 
         List<Part> parts = NotificationProgressBar.processAndConvertToDrawableParts(
-                segments, points, progress, isStyledByProgress);
+                segments, points, progress, progressMax, isStyledByProgress);
 
         List<Part> expected = new ArrayList<>(List.of(new Segment(1f, Color.RED)));
 
@@ -122,10 +149,11 @@
         segments.add(new ProgressStyle.Segment(100));
         List<ProgressStyle.Point> points = new ArrayList<>();
         int progress = 150;
+        int progressMax = 100;
         boolean isStyledByProgress = true;
 
         NotificationProgressBar.processAndConvertToDrawableParts(segments, points, progress,
-                isStyledByProgress);
+                progressMax, isStyledByProgress);
     }
 
     @Test(expected = IllegalArgumentException.class)
@@ -135,35 +163,11 @@
         List<ProgressStyle.Point> points = new ArrayList<>();
         points.add(new ProgressStyle.Point(-50).setColor(Color.RED));
         int progress = 50;
+        int progressMax = 100;
         boolean isStyledByProgress = true;
 
         NotificationProgressBar.processAndConvertToDrawableParts(segments, points, progress,
-                isStyledByProgress);
-    }
-
-    @Test(expected = IllegalArgumentException.class)
-    public void processAndConvertToDrawableParts_pointPositionIsZero() {
-        List<ProgressStyle.Segment> segments = new ArrayList<>();
-        segments.add(new ProgressStyle.Segment(100));
-        List<ProgressStyle.Point> points = new ArrayList<>();
-        points.add(new ProgressStyle.Point(0).setColor(Color.RED));
-        int progress = 50;
-        boolean isStyledByProgress = true;
-
-        NotificationProgressBar.processAndConvertToDrawableParts(segments, points, progress,
-                isStyledByProgress);
-    }
-
-    @Test(expected = IllegalArgumentException.class)
-    public void processAndConvertToDrawableParts_pointPositionAtMax() {
-        List<ProgressStyle.Segment> segments = new ArrayList<>();
-        segments.add(new ProgressStyle.Segment(100));
-        List<ProgressStyle.Point> points = new ArrayList<>();
-        points.add(new ProgressStyle.Point(100).setColor(Color.RED));
-        int progress = 50;
-        boolean isStyledByProgress = true;
-
-        NotificationProgressBar.processAndConvertToDrawableParts(segments, points, progress,
+                progressMax,
                 isStyledByProgress);
     }
 
@@ -174,9 +178,11 @@
         List<ProgressStyle.Point> points = new ArrayList<>();
         points.add(new ProgressStyle.Point(150).setColor(Color.RED));
         int progress = 50;
+        int progressMax = 100;
         boolean isStyledByProgress = true;
 
         NotificationProgressBar.processAndConvertToDrawableParts(segments, points, progress,
+                progressMax,
                 isStyledByProgress);
     }
 
@@ -187,10 +193,11 @@
         segments.add(new ProgressStyle.Segment(50).setColor(Color.GREEN));
         List<ProgressStyle.Point> points = new ArrayList<>();
         int progress = 60;
+        int progressMax = 100;
         boolean isStyledByProgress = true;
 
         List<Part> parts = NotificationProgressBar.processAndConvertToDrawableParts(
-                segments, points, progress, isStyledByProgress);
+                segments, points, progress, progressMax, isStyledByProgress);
 
         // Colors with 50% opacity
         int fadedGreen = 0x7F00FF00;
@@ -213,6 +220,7 @@
         points.add(new ProgressStyle.Point(60).setColor(Color.BLUE));
         points.add(new ProgressStyle.Point(75).setColor(Color.YELLOW));
         int progress = 60;
+        int progressMax = 100;
         boolean isStyledByProgress = true;
 
         // Colors with 50% opacity
@@ -231,7 +239,7 @@
                 new Segment(0.25f, fadedBlue, true)));
 
         List<Part> parts = NotificationProgressBar.processAndConvertToDrawableParts(
-                segments, points, progress, isStyledByProgress);
+                segments, points, progress, progressMax, isStyledByProgress);
 
         assertThat(parts).isEqualTo(expected);
     }
@@ -247,10 +255,11 @@
         points.add(new ProgressStyle.Point(60).setColor(Color.BLUE));
         points.add(new ProgressStyle.Point(75).setColor(Color.YELLOW));
         int progress = 60;
+        int progressMax = 100;
         boolean isStyledByProgress = true;
 
         List<Part> parts = NotificationProgressBar.processAndConvertToDrawableParts(
-                segments, points, progress, isStyledByProgress);
+                segments, points, progress, progressMax, isStyledByProgress);
 
         // Colors with 50% opacity
         int fadedGreen = 0x7F00FF00;
@@ -281,10 +290,11 @@
         points.add(new ProgressStyle.Point(25).setColor(Color.BLUE));
         points.add(new ProgressStyle.Point(75).setColor(Color.YELLOW));
         int progress = 60;
+        int progressMax = 100;
         boolean isStyledByProgress = false;
 
         List<Part> parts = NotificationProgressBar.processAndConvertToDrawableParts(
-                segments, points, progress, isStyledByProgress);
+                segments, points, progress, progressMax, isStyledByProgress);
 
         List<Part> expected = new ArrayList<>(List.of(
                 new Segment(0.15f, Color.RED),
diff --git a/core/tests/overlaytests/handle_config_change/Android.bp b/core/tests/overlaytests/handle_config_change/Android.bp
index 2b31d0a..42b60e8 100644
--- a/core/tests/overlaytests/handle_config_change/Android.bp
+++ b/core/tests/overlaytests/handle_config_change/Android.bp
@@ -38,7 +38,7 @@
         "device-tests",
     ],
     // All APKs required by the tests
-    data: [
+    device_common_data: [
         ":OverlayResApp",
     ],
     per_testcase_directory: true,
diff --git a/core/tests/vibrator/src/android/os/VibrationEffectTest.java b/core/tests/vibrator/src/android/os/VibrationEffectTest.java
index 8acf2ed..beb6985 100644
--- a/core/tests/vibrator/src/android/os/VibrationEffectTest.java
+++ b/core/tests/vibrator/src/android/os/VibrationEffectTest.java
@@ -46,6 +46,7 @@
 import android.os.vibrator.PrimitiveSegment;
 import android.os.vibrator.StepSegment;
 import android.os.vibrator.VibrationEffectSegment;
+import android.platform.test.annotations.EnableFlags;
 import android.platform.test.annotations.RequiresFlagsEnabled;
 
 import com.android.internal.R;
@@ -401,6 +402,32 @@
     }
 
     @Test
+    @EnableFlags(Flags.FLAG_NORMALIZED_PWLE_EFFECTS)
+    public void computeLegacyPattern_repeatingEffect() {
+        VibrationEffect repeatingEffect = VibrationEffect.createRepeatingEffect(TEST_ONE_SHOT,
+                TEST_WAVEFORM);
+        assertNull(repeatingEffect.computeCreateWaveformOffOnTimingsOrNull());
+
+        repeatingEffect = VibrationEffect.createRepeatingEffect(TEST_WAVEFORM);
+        assertNull(repeatingEffect.computeCreateWaveformOffOnTimingsOrNull());
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_NORMALIZED_PWLE_EFFECTS)
+    public void computeLegacyPattern_effectsViaStartWaveformEnvelope() {
+        // Effects created via startWaveformEnvelope are not expected to be converted to long[]
+        // patterns, as they are not configured to always play with the default amplitude.
+        VibrationEffect effect = VibrationEffect.startWaveformEnvelope()
+                .addControlPoint(/*amplitude=*/ 0.0f, /*frequencyHz=*/ 60f, /*timeMillis=*/ 20)
+                .addControlPoint(/*amplitude=*/ 0.3f, /*frequencyHz=*/ 100f, /*timeMillis=*/ 50)
+                .addControlPoint(/*amplitude=*/ 0.4f, /*frequencyHz=*/ 120f, /*timeMillis=*/ 80)
+                .addControlPoint(/*amplitude=*/ 0.0f, /*frequencyHz=*/ 120f, /*timeMillis=*/ 40)
+                .build();
+
+        assertNull(effect.computeCreateWaveformOffOnTimingsOrNull());
+    }
+
+    @Test
     public void computeLegacyPattern_effectsViaStartWaveform() {
         // Effects created via startWaveform are not expected to be converted to long[] patterns, as
         // they are not configured to always play with the default amplitude.
@@ -510,6 +537,17 @@
     }
 
     @Test
+    @EnableFlags(Flags.FLAG_NORMALIZED_PWLE_EFFECTS)
+    public void cropToLength_repeatingEffect() {
+        VibrationEffect repeatingEffect = VibrationEffect.createRepeatingEffect(TEST_ONE_SHOT,
+                TEST_WAVEFORM);
+        assertThat(repeatingEffect.cropToLengthOrNull(1)).isNull();
+
+        repeatingEffect = VibrationEffect.createRepeatingEffect(TEST_WAVEFORM);
+        assertThat(repeatingEffect.cropToLengthOrNull(1)).isNull();
+    }
+
+    @Test
     public void getRingtones_noPrebakedRingtones() {
         Resources r = mockRingtoneResources(new String[0]);
         Context context = mockContext(r);
@@ -595,6 +633,69 @@
     }
 
     @Test
+    @EnableFlags(Flags.FLAG_NORMALIZED_PWLE_EFFECTS)
+    public void testValidateWaveformEnvelopeBuilder() {
+        VibrationEffect.startWaveformEnvelope()
+                .addControlPoint(/*amplitude=*/ 0.0f, /*frequencyHz=*/ 60f, /*timeMillis=*/ 20)
+                .addControlPoint(/*amplitude=*/ 0.3f, /*frequencyHz=*/ 100f, /*timeMillis=*/ 50)
+                .addControlPoint(/*amplitude=*/ 0.4f, /*frequencyHz=*/ 120f, /*timeMillis=*/ 80)
+                .addControlPoint(/*amplitude=*/ 0.0f, /*frequencyHz=*/ 120f, /*timeMillis=*/ 40)
+                .build()
+                .validate();
+
+        VibrationEffect.createRepeatingEffect(
+                /*preamble=*/ VibrationEffect.startWaveformEnvelope()
+                        .addControlPoint(/*amplitude=*/ 0.0f, /*frequencyHz=*/ 60f,
+                                /*timeMillis=*/ 20)
+                        .addControlPoint(/*amplitude=*/ 0.3f, /*frequencyHz=*/ 100f,
+                                /*timeMillis=*/ 50)
+                        .build(),
+                /*repeatingEffect=*/ VibrationEffect.startWaveformEnvelope()
+                        .addControlPoint(/*amplitude=*/ 0.0f, /*frequencyHz=*/ 60f,
+                                /*timeMillis=*/ 20)
+                        .addControlPoint(/*amplitude=*/ 0.5f, /*frequencyHz=*/ 150f,
+                                /*timeMillis=*/ 100)
+                        .build()
+        ).validate();
+
+        VibrationEffect.createRepeatingEffect(
+                /*effect=*/ VibrationEffect.startWaveformEnvelope()
+                        .addControlPoint(/*amplitude=*/ 0.0f, /*frequencyHz=*/ 60f,
+                                /*timeMillis=*/ 20)
+                        .addControlPoint(/*amplitude=*/ 0.5f, /*frequencyHz=*/ 150f,
+                                /*timeMillis=*/ 100)
+                        .build()
+        ).validate();
+
+        assertThrows(IllegalStateException.class,
+                () -> VibrationEffect.startWaveformEnvelope().build().validate());
+        assertThrows(IllegalArgumentException.class,
+                () -> VibrationEffect.startWaveformEnvelope()
+                        .addControlPoint(/*amplitude=*/ -1.0f, /*frequencyHz=*/ 60f,
+                                /*timeMillis=*/ 20)
+                        .build()
+                        .validate());
+        assertThrows(IllegalArgumentException.class,
+                () -> VibrationEffect.startWaveformEnvelope()
+                        .addControlPoint(/*amplitude=*/ 1.1f, /*frequencyHz=*/ 60f,
+                                /*timeMillis=*/ 20)
+                        .build()
+                        .validate());
+        assertThrows(IllegalArgumentException.class,
+                () -> VibrationEffect.startWaveformEnvelope()
+                        .addControlPoint(/*amplitude=*/ 0.8f, /*frequencyHz=*/ 0f,
+                                /*timeMillis=*/ 20)
+                        .build()
+                        .validate());
+        assertThrows(IllegalArgumentException.class,
+                () -> VibrationEffect.startWaveformEnvelope()
+                        .addControlPoint(/*amplitude=*/ 0.8f, /*frequencyHz=*/ 100f,
+                                /*timeMillis=*/ 0)
+                        .build()
+                        .validate());
+    }
+
+    @Test
     public void testValidateWaveformBuilder() {
         // Cover builder methods
         VibrationEffect.startWaveform(targetAmplitude(1))
@@ -670,6 +771,21 @@
                 .repeatEffectIndefinitely(TEST_ONE_SHOT)
                 .compose()
                 .validate();
+        VibrationEffect.startComposition()
+                .addEffect(TEST_ONE_SHOT)
+                .addPrimitive(VibrationEffect.Composition.PRIMITIVE_LOW_TICK)
+                .addEffect(VibrationEffect.createRepeatingEffect(
+                        /*preamble=*/ VibrationEffect.createPredefined(
+                                VibrationEffect.EFFECT_DOUBLE_CLICK),
+                        /*repeatingEffect=*/ TEST_WAVEFORM))
+                .compose()
+                .validate();
+        VibrationEffect.startComposition()
+                .addEffect(TEST_ONE_SHOT)
+                .addPrimitive(VibrationEffect.Composition.PRIMITIVE_LOW_TICK)
+                .addEffect(VibrationEffect.createRepeatingEffect(TEST_WAVEFORM))
+                .compose()
+                .validate();
 
         // Make sure class summary javadoc examples compile and are valid.
         // NOTE: IF THIS IS UPDATED, PLEASE ALSO UPDATE Composition javadocs.
@@ -732,6 +848,31 @@
                         .addEffect(TEST_ONE_SHOT)
                         .compose()
                         .validate());
+
+        assertThrows(UnreachableAfterRepeatingIndefinitelyException.class,
+                () -> VibrationEffect.startComposition()
+                        .addEffect(VibrationEffect.createRepeatingEffect(
+                                /*preamble=*/ VibrationEffect.createPredefined(
+                                        VibrationEffect.EFFECT_DOUBLE_CLICK),
+                                /*repeatingEffect=*/ TEST_WAVEFORM))
+                        .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK)
+                        .compose()
+                        .validate());
+        assertThrows(UnreachableAfterRepeatingIndefinitelyException.class,
+                () -> VibrationEffect.startComposition()
+                        .addEffect(VibrationEffect.createRepeatingEffect(
+                                        /*preamble=*/ VibrationEffect.createPredefined(
+                                                VibrationEffect.EFFECT_DOUBLE_CLICK),
+                                        /*repeatingEffect=*/ TEST_WAVEFORM))
+                        .addEffect(TEST_ONE_SHOT)
+                        .compose()
+                        .validate());
+        assertThrows(UnreachableAfterRepeatingIndefinitelyException.class,
+                () -> VibrationEffect.startComposition()
+                        .addEffect(VibrationEffect.createRepeatingEffect(TEST_WAVEFORM))
+                        .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK)
+                        .compose()
+                        .validate());
     }
 
     @Test
@@ -1190,6 +1331,13 @@
                 .addTransition(Duration.ofMillis(500), targetAmplitude(0))
                 .build()
                 .isHapticFeedbackCandidate());
+        assertFalse(VibrationEffect.startWaveformEnvelope()
+                .addControlPoint(/*amplitude=*/ 0.0f, /*frequencyHz=*/ 60f, /*timeMillis=*/ 200)
+                .addControlPoint(/*amplitude=*/ 0.3f, /*frequencyHz=*/ 100f, /*timeMillis=*/ 500)
+                .addControlPoint(/*amplitude=*/ 0.4f, /*frequencyHz=*/ 120f, /*timeMillis=*/ 800)
+                .addControlPoint(/*amplitude=*/ 0.0f, /*frequencyHz=*/ 120f, /*timeMillis=*/ 400)
+                .build()
+                .isHapticFeedbackCandidate());
     }
 
     @Test
@@ -1203,6 +1351,12 @@
                 .addTransition(Duration.ofMillis(300), targetAmplitude(0))
                 .build()
                 .isHapticFeedbackCandidate());
+        assertTrue(VibrationEffect.startWaveformEnvelope()
+                .addControlPoint(/*amplitude=*/ 0.3f, /*frequencyHz=*/ 100f, /*timeMillis=*/ 500)
+                .addControlPoint(/*amplitude=*/ 0.4f, /*frequencyHz=*/ 120f, /*timeMillis=*/ 400)
+                .addControlPoint(/*amplitude=*/ 0.0f, /*frequencyHz=*/ 120f, /*timeMillis=*/ 100)
+                .build()
+                .isHapticFeedbackCandidate());
     }
 
     @Test
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index debd0df..56e55df 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -661,4 +661,8 @@
         <permission name="android.permission.BATTERY_STATS"/>
         <permission name="android.permission.ENTER_TRADE_IN_MODE"/>
     </privapp-permissions>
+
+    <privapp-permissions package="com.android.multiuser">
+        <permission name="android.permission.MANAGE_USERS"/>
+    </privapp-permissions>
 </permissions>
diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java
index 461a5ae..dfded73 100644
--- a/graphics/java/android/graphics/Bitmap.java
+++ b/graphics/java/android/graphics/Bitmap.java
@@ -102,6 +102,8 @@
 
     private static volatile int sDefaultDensity = -1;
 
+    private long mId;
+
     /**
      * For backwards compatibility, allows the app layer to change the default
      * density when running old apps.
@@ -152,18 +154,19 @@
     Bitmap(long nativeBitmap, int width, int height, int density,
             boolean requestPremultiplied, byte[] ninePatchChunk,
             NinePatch.InsetStruct ninePatchInsets) {
-        this(nativeBitmap, width, height, density, requestPremultiplied, ninePatchChunk,
+        this(0, nativeBitmap, width, height, density, requestPremultiplied, ninePatchChunk,
                 ninePatchInsets, true);
     }
 
     // called from JNI and Bitmap_Delegate.
-    Bitmap(long nativeBitmap, int width, int height, int density,
+    Bitmap(long id, long nativeBitmap, int width, int height, int density,
             boolean requestPremultiplied, byte[] ninePatchChunk,
             NinePatch.InsetStruct ninePatchInsets, boolean fromMalloc) {
         if (nativeBitmap == 0) {
             throw new RuntimeException("internal error: native bitmap is 0");
         }
 
+        mId = id;
         mWidth = width;
         mHeight = height;
         mRequestPremultiplied = requestPremultiplied;
diff --git a/keystore/java/android/security/keystore/KeyProperties.java b/keystore/java/android/security/keystore/KeyProperties.java
index 7f936f2..344d19a 100644
--- a/keystore/java/android/security/keystore/KeyProperties.java
+++ b/keystore/java/android/security/keystore/KeyProperties.java
@@ -23,9 +23,6 @@
 import android.annotation.SystemApi;
 import android.os.Process;
 import android.security.keymaster.KeymasterDefs;
-
-import libcore.util.EmptyArray;
-
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.security.spec.AlgorithmParameterSpec;
@@ -33,6 +30,7 @@
 import java.security.spec.MGF1ParameterSpec;
 import java.util.Collection;
 import java.util.Locale;
+import libcore.util.EmptyArray;
 
 /**
  * Properties of <a href="{@docRoot}training/articles/keystore.html">Android Keystore</a> keys.
@@ -116,7 +114,7 @@
     public static final int PURPOSE_AGREE_KEY = 1 << 6;
 
     /**
-     * Purpose of key: Signing attestaions. This purpose is incompatible with all others, meaning
+     * Purpose of key: Signing attestations. This purpose is incompatible with all others, meaning
      * that when generating a key with PURPOSE_ATTEST_KEY, no other purposes may be specified. In
      * addition, PURPOSE_ATTEST_KEY may not be specified for imported keys.
      */
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/common/ExtensionHelper.java b/libs/WindowManager/Jetpack/src/androidx/window/common/ExtensionHelper.java
index f466d60..19f837c 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/common/ExtensionHelper.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/common/ExtensionHelper.java
@@ -23,7 +23,6 @@
 import android.app.WindowConfiguration;
 import android.content.Context;
 import android.graphics.Rect;
-import android.hardware.display.DisplayManagerGlobal;
 import android.util.RotationUtils;
 import android.view.DisplayInfo;
 import android.view.Surface;
@@ -31,7 +30,6 @@
 
 import androidx.annotation.NonNull;
 import androidx.annotation.UiContext;
-import androidx.annotation.VisibleForTesting;
 
 /**
  * Util class for both Sidecar and Extensions.
@@ -46,24 +44,15 @@
      * Rotates the input rectangle specified in default display orientation to the current display
      * rotation.
      *
-     * @param displayId the display id.
+     * @param displayInfo the display information.
      * @param rotation the target rotation relative to the default display orientation.
      * @param inOutRect the input/output Rect as specified in the default display orientation.
      */
-    public static void rotateRectToDisplayRotation(
-            int displayId, @Surface.Rotation int rotation, @NonNull Rect inOutRect) {
-        final DisplayManagerGlobal dmGlobal = DisplayManagerGlobal.getInstance();
-        final DisplayInfo displayInfo = dmGlobal.getDisplayInfo(displayId);
-
-        rotateRectToDisplayRotation(displayInfo, rotation, inOutRect);
-    }
-
     // We suppress the Lint error CheckResult for Rect#intersect because in case the displayInfo and
     // folding features are out of sync, e.g. when a foldable devices is unfolding, it is acceptable
     // to provide the original folding feature Rect even if they don't intersect.
     @SuppressLint("RectIntersectReturnValueIgnored")
-    @VisibleForTesting
-    static void rotateRectToDisplayRotation(@NonNull DisplayInfo displayInfo,
+    public static void rotateRectToDisplayRotation(@NonNull DisplayInfo displayInfo,
             @Surface.Rotation int rotation, @NonNull Rect inOutRect) {
         // The inOutRect is specified in the default display orientation, so here we need to get
         // the display width and height in the default orientation to perform the intersection and
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java
index 556da37..5ce73b9 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java
@@ -34,11 +34,14 @@
 import android.content.ContextWrapper;
 import android.content.res.Configuration;
 import android.graphics.Rect;
+import android.hardware.display.DisplayManagerGlobal;
 import android.os.Bundle;
 import android.os.IBinder;
 import android.os.StrictMode;
 import android.util.ArrayMap;
 import android.util.Log;
+import android.view.DisplayInfo;
+import android.view.Surface;
 
 import androidx.annotation.GuardedBy;
 import androidx.annotation.NonNull;
@@ -96,8 +99,29 @@
 
     private final SupportedWindowFeatures mSupportedWindowFeatures;
 
+    private final DisplayStateProvider mDisplayStateProvider;
+
     public WindowLayoutComponentImpl(@NonNull Context context,
             @NonNull DeviceStateManagerFoldingFeatureProducer foldingFeatureProducer) {
+        this(context, foldingFeatureProducer, new DisplayStateProvider() {
+            @Override
+            public int getDisplayRotation(@NonNull WindowConfiguration windowConfiguration) {
+                return windowConfiguration.getDisplayRotation();
+            }
+
+            @NonNull
+            @Override
+            public DisplayInfo getDisplayInfo(int displayId) {
+                return DisplayManagerGlobal.getInstance().getDisplayInfo(displayId);
+            }
+        });
+    }
+
+    @VisibleForTesting
+    WindowLayoutComponentImpl(@NonNull Context context,
+            @NonNull DeviceStateManagerFoldingFeatureProducer foldingFeatureProducer,
+            @NonNull DisplayStateProvider displayStateProvider) {
+        mDisplayStateProvider = displayStateProvider;
         ((Application) context.getApplicationContext())
                 .registerActivityLifecycleCallbacks(new NotifyOnConfigurationChanged());
         mFoldingFeatureProducer = foldingFeatureProducer;
@@ -145,21 +169,7 @@
                     || containsConsumer(consumer)) {
                 return;
             }
-            final IllegalArgumentException exception = new IllegalArgumentException(
-                    "Context must be a UI Context with display association, which should be"
-                    + " an Activity, WindowContext or InputMethodService");
-            if (!context.isUiContext()) {
-                throw exception;
-            }
-            if (context.getAssociatedDisplayId() == INVALID_DISPLAY) {
-                // This is to identify if #isUiContext of a non-UI Context is overridden.
-                // #isUiContext is more likely to be overridden than #getAssociatedDisplayId
-                // since #isUiContext is a public API.
-                StrictMode.onIncorrectContextUsed("The registered Context is a UI Context "
-                        + "but not associated with any display. "
-                        + "This Context may not receive any WindowLayoutInfo update. "
-                        + dumpAllBaseContextToString(context), exception);
-            }
+            assertUiContext(context);
             Log.d(TAG, "Register WindowLayoutInfoListener on "
                     + dumpAllBaseContextToString(context));
             mFoldingFeatureProducer.getData((features) -> {
@@ -339,6 +349,7 @@
     @Override
     @NonNull
     public WindowLayoutInfo getCurrentWindowLayoutInfo(@NonNull @UiContext Context context) {
+        assertUiContext(context);
         synchronized (mLock) {
             return getWindowLayoutInfo(context, mLastReportedFoldingFeatures);
         }
@@ -353,6 +364,25 @@
         return mSupportedWindowFeatures;
     }
 
+    private void assertUiContext(@NonNull Context context) {
+        final IllegalArgumentException exception = new IllegalArgumentException(
+                "Context must be a UI Context with display association, which should be "
+                        + "an Activity, WindowContext or InputMethodService");
+        if (!context.isUiContext()) {
+            throw exception;
+        }
+        if (context.getAssociatedDisplayId() == INVALID_DISPLAY) {
+            // This is to identify if #isUiContext of a non-UI Context is overridden.
+            // #isUiContext is more likely to be overridden than #getAssociatedDisplayId
+            // since #isUiContext is a public API.
+            StrictMode.onIncorrectContextUsed("The given context is a UI context, "
+                    + "but it is not associated with any display. "
+                    + "This context may not receive WindowLayoutInfo updates and "
+                    + "may get an empty WindowLayoutInfo return value. "
+                    + dumpAllBaseContextToString(context), exception);
+        }
+    }
+
     /** @see #getWindowLayoutInfo(Context, List) */
     private WindowLayoutInfo getWindowLayoutInfo(int displayId,
             @NonNull WindowConfiguration windowConfiguration,
@@ -401,15 +431,16 @@
 
         // We will transform the feature bounds to the Activity window, so using the rotation
         // from the same source (WindowConfiguration) to make sure they are synchronized.
-        final int rotation = windowConfiguration.getDisplayRotation();
+        final int rotation = mDisplayStateProvider.getDisplayRotation(windowConfiguration);
+        final DisplayInfo displayInfo = mDisplayStateProvider.getDisplayInfo(displayId);
 
         for (CommonFoldingFeature baseFeature : storedFeatures) {
             Integer state = convertToExtensionState(baseFeature.getState());
             if (state == null) {
                 continue;
             }
-            Rect featureRect = baseFeature.getRect();
-            rotateRectToDisplayRotation(displayId, rotation, featureRect);
+            final Rect featureRect = baseFeature.getRect();
+            rotateRectToDisplayRotation(displayInfo, rotation, featureRect);
             transformToWindowSpaceRect(windowConfiguration, featureRect);
 
             if (isZero(featureRect)) {
@@ -530,4 +561,13 @@
         public void onLowMemory() {
         }
     }
+
+    @VisibleForTesting
+    interface DisplayStateProvider {
+        @Surface.Rotation
+        int getDisplayRotation(@NonNull WindowConfiguration windowConfiguration);
+
+        @NonNull
+        DisplayInfo getDisplayInfo(int displayId);
+    }
 }
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SidecarHelper.java b/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SidecarHelper.java
index 6e0e711..3b30f1b 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SidecarHelper.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SidecarHelper.java
@@ -24,7 +24,9 @@
 import android.app.Activity;
 import android.app.ActivityThread;
 import android.graphics.Rect;
+import android.hardware.display.DisplayManagerGlobal;
 import android.os.IBinder;
+import android.view.DisplayInfo;
 
 import androidx.window.common.layout.CommonFoldingFeature;
 
@@ -96,13 +98,18 @@
             return Collections.emptyList();
         }
 
-        final List<SidecarDisplayFeature> features = new ArrayList<>();
+        // We will transform the feature bounds to the Activity window, so using the rotation
+        // from the same source (WindowConfiguration) to make sure they are synchronized.
         final int rotation = activity.getResources().getConfiguration().windowConfiguration
                 .getDisplayRotation();
+        final DisplayInfo displayInfo =
+                DisplayManagerGlobal.getInstance().getDisplayInfo(displayId);
+
+        final List<SidecarDisplayFeature> features = new ArrayList<>();
         for (CommonFoldingFeature baseFeature : featureList) {
             final SidecarDisplayFeature feature = new SidecarDisplayFeature();
             final Rect featureRect = baseFeature.getRect();
-            rotateRectToDisplayRotation(displayId, rotation, featureRect);
+            rotateRectToDisplayRotation(displayInfo, rotation, featureRect);
             transformToWindowSpaceRect(activity, featureRect);
             feature.setRect(featureRect);
             feature.setType(baseFeature.getType());
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/layout/WindowLayoutComponentImplTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/layout/WindowLayoutComponentImplTest.java
index ed4eddf..0643feb 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/layout/WindowLayoutComponentImplTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/layout/WindowLayoutComponentImplTest.java
@@ -26,6 +26,8 @@
 import android.graphics.Rect;
 import android.platform.test.annotations.Presubmit;
 import android.view.Display;
+import android.view.DisplayInfo;
+import android.view.Surface;
 
 import androidx.annotation.NonNull;
 import androidx.test.core.app.ApplicationProvider;
@@ -72,6 +74,11 @@
         mWindowLayoutComponent.onDisplayFeaturesChanged(Collections.emptyList());
     }
 
+    @Test(expected = IllegalArgumentException.class)
+    public void testAddWindowLayoutListener_nonUiContext_throwsError() {
+        mWindowLayoutComponent.addWindowLayoutInfoListener(mAppContext, info -> {});
+    }
+
     @Test
     public void testGetCurrentWindowLayoutInfo_noFoldingFeature_returnsEmptyList() {
         final Context testUiContext = new TestUiContext(mAppContext);
@@ -88,6 +95,29 @@
         final WindowConfiguration windowConfiguration =
                 testUiContext.getResources().getConfiguration().windowConfiguration;
         final Rect featureRect = windowConfiguration.getBounds();
+        // Mock DisplayStateProvider to control rotation and DisplayInfo, preventing dependency on
+        // the real device orientation or display configuration. This improves test reliability on
+        // devices like foldables or tablets that might have varying configurations.
+        final WindowLayoutComponentImpl.DisplayStateProvider displayStateProvider =
+                new WindowLayoutComponentImpl.DisplayStateProvider() {
+                    @Override
+                    public int getDisplayRotation(
+                            @NonNull WindowConfiguration windowConfiguration) {
+                        return Surface.ROTATION_0;
+                    }
+
+                    @NonNull
+                    @Override
+                    public DisplayInfo getDisplayInfo(int displayId) {
+                        final DisplayInfo displayInfo = new DisplayInfo();
+                        displayInfo.logicalWidth = featureRect.width();
+                        displayInfo.logicalHeight = featureRect.height();
+                        return displayInfo;
+                    }
+                };
+        mWindowLayoutComponent = new WindowLayoutComponentImpl(mAppContext,
+                mock(DeviceStateManagerFoldingFeatureProducer.class),
+                displayStateProvider);
         final CommonFoldingFeature foldingFeature = new CommonFoldingFeature(
                 CommonFoldingFeature.COMMON_TYPE_HINGE,
                 CommonFoldingFeature.COMMON_STATE_FLAT,
@@ -102,12 +132,9 @@
                 featureRect, FoldingFeature.TYPE_HINGE, FoldingFeature.STATE_FLAT));
     }
 
-    @Test
-    public void testGetCurrentWindowLayoutInfo_nonUiContext_returnsEmptyList() {
-        final WindowLayoutInfo layoutInfo =
-                mWindowLayoutComponent.getCurrentWindowLayoutInfo(mAppContext);
-
-        assertThat(layoutInfo.getDisplayFeatures()).isEmpty();
+    @Test(expected = IllegalArgumentException.class)
+    public void testGetCurrentWindowLayoutInfo_nonUiContext_throwsError() {
+        mWindowLayoutComponent.getCurrentWindowLayoutInfo(mAppContext);
     }
 
     /**
diff --git a/libs/WindowManager/Shell/Android.bp b/libs/WindowManager/Shell/Android.bp
index f36dff0..42188de 100644
--- a/libs/WindowManager/Shell/Android.bp
+++ b/libs/WindowManager/Shell/Android.bp
@@ -157,6 +157,13 @@
 }
 
 filegroup {
+    name: "wm_shell-shared-utils",
+    srcs: [
+        "shared/src/com/android/wm/shell/shared/TransitionUtil.java",
+    ],
+}
+
+filegroup {
     name: "wm_shell-shared-aidls",
 
     srcs: [
diff --git a/libs/WindowManager/Shell/multivalentScreenshotTests/Android.bp b/libs/WindowManager/Shell/multivalentScreenshotTests/Android.bp
index b6db6d9..61c09f2 100644
--- a/libs/WindowManager/Shell/multivalentScreenshotTests/Android.bp
+++ b/libs/WindowManager/Shell/multivalentScreenshotTests/Android.bp
@@ -29,6 +29,8 @@
     static_libs: [
         "WindowManager-Shell",
         "platform-screenshot-diff-core",
+        "ScreenshotComposeUtilsLib", // ComposableScreenshotTestRule & Theme.PlatformUi.Screenshot
+        "SystemUI-res", // Theme.SystemUI (dragged in by ScreenshotComposeUtilsLib)
     ],
     asset_dirs: ["goldens/robolectric"],
     manifest: "AndroidManifestRobolectric.xml",
@@ -63,6 +65,8 @@
     ],
     static_libs: [
         "WindowManager-Shell",
+        "ScreenshotComposeUtilsLib", // ComposableScreenshotTestRule & Theme.PlatformUi.Screenshot
+        "SystemUI-res", // Theme.SystemUI (dragged in by ScreenshotComposeUtilsLib)
         "junit",
         "androidx.test.runner",
         "androidx.test.rules",
diff --git a/libs/WindowManager/Shell/multivalentScreenshotTests/AndroidManifestRobolectric.xml b/libs/WindowManager/Shell/multivalentScreenshotTests/AndroidManifestRobolectric.xml
index b4bdaea..72d0d5e4 100644
--- a/libs/WindowManager/Shell/multivalentScreenshotTests/AndroidManifestRobolectric.xml
+++ b/libs/WindowManager/Shell/multivalentScreenshotTests/AndroidManifestRobolectric.xml
@@ -18,6 +18,7 @@
     <application android:debuggable="true" android:supportsRtl="true">
         <activity
             android:name="platform.test.screenshot.ScreenshotActivity"
+            android:theme="@style/Theme.PlatformUi.Screenshot"
             android:exported="true">
         </activity>
     </application>
diff --git a/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/phone/dark_portrait_bubbles_education.png b/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/phone/dark_portrait_bubbles_education.png
index 736bca7..15af624 100644
--- a/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/phone/dark_portrait_bubbles_education.png
+++ b/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/phone/dark_portrait_bubbles_education.png
Binary files differ
diff --git a/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/phone/light_portrait_bubbles_education.png b/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/phone/light_portrait_bubbles_education.png
index 736bca7..85ce24b 100644
--- a/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/phone/light_portrait_bubbles_education.png
+++ b/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/phone/light_portrait_bubbles_education.png
Binary files differ
diff --git a/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/robolectric/phone/dark_portrait_bubbles_education.png b/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/robolectric/phone/dark_portrait_bubbles_education.png
index e540b45..a1d0e7b 100644
--- a/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/robolectric/phone/dark_portrait_bubbles_education.png
+++ b/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/robolectric/phone/dark_portrait_bubbles_education.png
Binary files differ
diff --git a/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/robolectric/phone/light_portrait_bubbles_education.png b/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/robolectric/phone/light_portrait_bubbles_education.png
index e540b45..3bc2ae7 100644
--- a/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/robolectric/phone/light_portrait_bubbles_education.png
+++ b/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/robolectric/phone/light_portrait_bubbles_education.png
Binary files differ
diff --git a/libs/WindowManager/Shell/multivalentScreenshotTests/src/com/android/wm/shell/bubbles/BubbleEducationViewScreenshotTest.kt b/libs/WindowManager/Shell/multivalentScreenshotTests/src/com/android/wm/shell/bubbles/BubbleEducationViewScreenshotTest.kt
index f09969d..8cf3ce9 100644
--- a/libs/WindowManager/Shell/multivalentScreenshotTests/src/com/android/wm/shell/bubbles/BubbleEducationViewScreenshotTest.kt
+++ b/libs/WindowManager/Shell/multivalentScreenshotTests/src/com/android/wm/shell/bubbles/BubbleEducationViewScreenshotTest.kt
@@ -15,10 +15,12 @@
  */
 package com.android.wm.shell.bubbles
 
+import android.graphics.Color
 import android.view.LayoutInflater
+import android.view.ViewGroup
+import com.android.wm.shell.R
 import com.android.wm.shell.shared.bubbles.BubblePopupView
 import com.android.wm.shell.testing.goldenpathmanager.WMShellGoldenPathManager
-import com.android.wm.shell.R
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -48,6 +50,10 @@
     fun bubblesEducation() {
         screenshotRule.screenshotTest("bubbles_education") { activity ->
             activity.actionBar?.hide()
+            // Set the background color of the activity to be something not from the theme to
+            // ensure good contrast between the education view and the background
+            val rootView = activity.window.decorView.findViewById(android.R.id.content) as ViewGroup
+            rootView.setBackgroundColor(Color.RED)
             val view =
                 LayoutInflater.from(activity)
                     .inflate(R.layout.bubble_bar_stack_education, null) as BubblePopupView
diff --git a/libs/WindowManager/Shell/multivalentTests/Android.bp b/libs/WindowManager/Shell/multivalentTests/Android.bp
index ee0d5bb..41d1b5c 100644
--- a/libs/WindowManager/Shell/multivalentTests/Android.bp
+++ b/libs/WindowManager/Shell/multivalentTests/Android.bp
@@ -53,6 +53,8 @@
         "mockito-robolectric-prebuilt",
         "mockito-kotlin2",
         "truth",
+        "flag-junit-base",
+        "flag-junit",
     ],
     auto_gen_config: true,
 }
diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleTaskViewTest.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleTaskViewTest.kt
index 398fd55..4214e6f 100644
--- a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleTaskViewTest.kt
+++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleTaskViewTest.kt
@@ -18,14 +18,19 @@
 
 import android.content.ComponentName
 import android.content.Context
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
+import android.platform.test.flag.junit.SetFlagsRule
 import androidx.test.core.app.ApplicationProvider
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
+import com.android.wm.shell.Flags
 import com.android.wm.shell.taskview.TaskView
 
 import com.google.common.truth.Truth.assertThat
 import com.google.common.util.concurrent.MoreExecutors.directExecutor
 import org.junit.Before
+import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.kotlin.mock
@@ -36,6 +41,9 @@
 @RunWith(AndroidJUnit4::class)
 class BubbleTaskViewTest {
 
+    @get:Rule
+    val setFlagsRule = SetFlagsRule()
+
     private lateinit var bubbleTaskView: BubbleTaskView
     private val context = ApplicationProvider.getApplicationContext<Context>()
     private lateinit var taskView: TaskView
@@ -75,14 +83,33 @@
         assertThat(actualComponentName).isEqualTo(componentName)
     }
 
+    @DisableFlags(Flags.FLAG_ENABLE_TASK_VIEW_CONTROLLER_CLEANUP)
     @Test
-    fun cleanup_invalidTaskId_doesNotRemoveTask() {
+    fun cleanup_flagOff_invalidTaskId_doesNotRemoveTask() {
         bubbleTaskView.cleanup()
         verify(taskView, never()).removeTask()
     }
 
+    @EnableFlags(Flags.FLAG_ENABLE_TASK_VIEW_CONTROLLER_CLEANUP)
     @Test
-    fun cleanup_validTaskId_removesTask() {
+    fun cleanup_flagOn_invalidTaskId_removesTask() {
+        bubbleTaskView.cleanup()
+        verify(taskView).removeTask()
+    }
+
+    @DisableFlags(Flags.FLAG_ENABLE_TASK_VIEW_CONTROLLER_CLEANUP)
+    @Test
+    fun cleanup_flagOff_validTaskId_removesTask() {
+        val componentName = ComponentName(context, "TestClass")
+        bubbleTaskView.listener.onTaskCreated(123, componentName)
+
+        bubbleTaskView.cleanup()
+        verify(taskView).removeTask()
+    }
+
+    @EnableFlags(Flags.FLAG_ENABLE_TASK_VIEW_CONTROLLER_CLEANUP)
+    @Test
+    fun cleanup_flagOn_validTaskId_removesTask() {
         val componentName = ComponentName(context, "TestClass")
         bubbleTaskView.listener.onTaskCreated(123, componentName)
 
diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleViewInfoTaskTest.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleViewInfoTaskTest.kt
index 712cc7c..776ea92 100644
--- a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleViewInfoTaskTest.kt
+++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleViewInfoTaskTest.kt
@@ -25,6 +25,7 @@
 import android.os.UserManager
 import android.view.IWindowManager
 import android.view.WindowManager
+import android.widget.FrameLayout
 import androidx.test.core.app.ApplicationProvider
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
@@ -56,7 +57,9 @@
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
+import org.mockito.kotlin.any
 import org.mockito.kotlin.mock
+import org.mockito.kotlin.whenever
 
 /** Test inflating bubbles with [BubbleViewInfoTask]. */
 @SmallTest
@@ -158,18 +161,23 @@
                 mock<BubbleProperties>()
             )
 
-        val bubbleStackViewManager = BubbleStackViewManager.fromBubbleController(bubbleController)
-        bubbleStackView =
-            BubbleStackView(
-                context,
-                bubbleStackViewManager,
-                bubblePositioner,
-                bubbleData,
-                surfaceSynchronizer,
-                FloatingContentCoordinator(),
-                bubbleController,
-                mainExecutor
-            )
+        // TODO: (b/371829099) - when optional overflow is no longer flagged we can enable this
+        //  again, something about the overflow being added uncovers an issue with Robolectric and
+        //  bitmaps; this is switched to a mock to work around that (b/375513387).
+//        val bubbleStackViewManager = BubbleStackViewManager.fromBubbleController(bubbleController)
+//        bubbleStackView = BubbleStackView(
+//                context,
+//                bubbleStackViewManager,
+//                bubblePositioner,
+//                bubbleData,
+//                surfaceSynchronizer,
+//                FloatingContentCoordinator(),
+//                bubbleController,
+//                mainExecutor
+//            )
+        bubbleStackView = mock<BubbleStackView>()
+        whenever(bubbleStackView.generateLayoutParams(any()))
+            .thenReturn(FrameLayout.LayoutParams(1000, 1000))
         expandedViewManager = BubbleExpandedViewManager.fromBubbleController(bubbleController)
     }
 
diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/FakeBubbleFactory.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/FakeBubbleFactory.kt
new file mode 100644
index 0000000..cb6fb62
--- /dev/null
+++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/FakeBubbleFactory.kt
@@ -0,0 +1,57 @@
+/*
+ * 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.bubbles
+
+import android.content.Context
+import android.content.pm.ShortcutInfo
+import android.content.res.Resources
+import com.android.wm.shell.bubbles.BubbleViewInfoTask.BubbleViewInfo
+import com.android.wm.shell.bubbles.bar.BubbleBarExpandedView
+import com.google.common.util.concurrent.MoreExecutors.directExecutor
+
+/** Helper to create a [Bubble] instance */
+class FakeBubbleFactory {
+
+    companion object {
+
+        fun createViewInfo(bubbleExpandedView: BubbleBarExpandedView): BubbleViewInfo {
+            return BubbleViewInfo().apply { bubbleBarExpandedView = bubbleExpandedView }
+        }
+
+        fun createChatBubbleWithViewInfo(
+            context: Context,
+            key: String = "key",
+            viewInfo: BubbleViewInfo,
+        ): Bubble {
+            val bubble =
+                Bubble(
+                    key,
+                    ShortcutInfo.Builder(context, "id").build(),
+                    100, /* desiredHeight */
+                    Resources.ID_NULL, /* desiredHeightResId */
+                    "title",
+                    0, /* taskId */
+                    null, /* locus */
+                    true, /* isDismissable */
+                    directExecutor(),
+                    directExecutor(),
+                ) {}
+            bubble.setViewInfo(viewInfo)
+            return bubble
+        }
+    }
+}
diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/animation/AnimatableScaleMatrixTest.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/animation/AnimatableScaleMatrixTest.kt
new file mode 100644
index 0000000..3ed3604
--- /dev/null
+++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/animation/AnimatableScaleMatrixTest.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.wm.shell.bubbles.animation
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/** Tests for [AnimatableScaleMatrix] */
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class AnimatableScaleMatrixTest {
+
+    @Test
+    fun test_equals_matricesWithSameValuesAreNotEqual() {
+        val matrix1 = AnimatableScaleMatrix().apply { setScale(0.5f, 0.5f) }
+        val matrix2 = AnimatableScaleMatrix().apply { setScale(0.5f, 0.5f) }
+        assertThat(matrix1).isNotEqualTo(matrix2)
+    }
+
+    @Test
+    fun test_hashCode_remainsSameIfMatrixUpdates() {
+        val matrix = AnimatableScaleMatrix().apply { setScale(0.5f, 0.5f) }
+        val hash1 = matrix.hashCode()
+        matrix.setScale(0.75f, 0.75f)
+        val hash2 = matrix.hashCode()
+
+        assertThat(hash1).isEqualTo(hash2)
+    }
+
+    @Test
+    fun test_hashCode_matricesWithSameValuesHaveDiffHashCode() {
+        val matrix1 = AnimatableScaleMatrix().apply { setScale(0.5f, 0.5f) }
+        val matrix2 = AnimatableScaleMatrix().apply { setScale(0.5f, 0.5f) }
+        assertThat(matrix1.hashCode()).isNotEqualTo(matrix2.hashCode())
+    }
+}
diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerViewTest.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerViewTest.kt
new file mode 100644
index 0000000..2fbf089
--- /dev/null
+++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerViewTest.kt
@@ -0,0 +1,293 @@
+/*
+ * 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.bubbles.bar
+
+import android.app.ActivityManager
+import android.content.Context
+import android.content.pm.LauncherApps
+import android.os.Handler
+import android.os.UserManager
+import android.view.IWindowManager
+import android.view.LayoutInflater
+import android.view.View
+import android.view.WindowManager
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
+import com.android.internal.logging.testing.UiEventLoggerFake
+import com.android.internal.protolog.ProtoLog
+import com.android.internal.statusbar.IStatusBarService
+import com.android.wm.shell.R
+import com.android.wm.shell.ShellTaskOrganizer
+import com.android.wm.shell.WindowManagerShellWrapper
+import com.android.wm.shell.bubbles.Bubble
+import com.android.wm.shell.bubbles.BubbleController
+import com.android.wm.shell.bubbles.BubbleData
+import com.android.wm.shell.bubbles.BubbleDataRepository
+import com.android.wm.shell.bubbles.BubbleEducationController
+import com.android.wm.shell.bubbles.BubbleExpandedViewManager
+import com.android.wm.shell.bubbles.BubbleLogger
+import com.android.wm.shell.bubbles.BubblePositioner
+import com.android.wm.shell.bubbles.BubbleTaskView
+import com.android.wm.shell.bubbles.BubbleTaskViewFactory
+import com.android.wm.shell.bubbles.Bubbles.SysuiProxy
+import com.android.wm.shell.bubbles.FakeBubbleFactory
+import com.android.wm.shell.bubbles.UiEventSubject.Companion.assertThat
+import com.android.wm.shell.bubbles.properties.BubbleProperties
+import com.android.wm.shell.bubbles.storage.BubblePersistentRepository
+import com.android.wm.shell.common.DisplayController
+import com.android.wm.shell.common.DisplayInsetsController
+import com.android.wm.shell.common.FloatingContentCoordinator
+import com.android.wm.shell.common.ShellExecutor
+import com.android.wm.shell.common.SyncTransactionQueue
+import com.android.wm.shell.common.TaskStackListenerImpl
+import com.android.wm.shell.shared.TransactionPool
+import com.android.wm.shell.shared.bubbles.BubbleBarLocation
+import com.android.wm.shell.sysui.ShellCommandHandler
+import com.android.wm.shell.sysui.ShellController
+import com.android.wm.shell.sysui.ShellInit
+import com.android.wm.shell.taskview.TaskView
+import com.android.wm.shell.taskview.TaskViewTaskController
+import com.android.wm.shell.taskview.TaskViewTransitions
+import com.android.wm.shell.transition.Transitions
+import com.google.common.truth.Truth.assertThat
+import java.util.Collections
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito.mock
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.whenever
+
+/** Tests for [BubbleBarLayerView] */
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class BubbleBarLayerViewTest {
+
+    private val context = ApplicationProvider.getApplicationContext<Context>()
+
+    private lateinit var bubbleBarLayerView: BubbleBarLayerView
+
+    private lateinit var uiEventLoggerFake: UiEventLoggerFake
+
+    private lateinit var bubble: Bubble
+
+    @Before
+    fun setUp() {
+        ProtoLog.REQUIRE_PROTOLOGTOOL = false
+        ProtoLog.init()
+
+        uiEventLoggerFake = UiEventLoggerFake()
+        val bubbleLogger = BubbleLogger(uiEventLoggerFake)
+
+        val mainExecutor = TestExecutor()
+        val bgExecutor = TestExecutor()
+
+        val windowManager = context.getSystemService(WindowManager::class.java)
+
+        val bubblePositioner = BubblePositioner(context, windowManager)
+        bubblePositioner.setShowingInBubbleBar(true)
+
+        val bubbleData =
+            BubbleData(
+                context,
+                bubbleLogger,
+                bubblePositioner,
+                BubbleEducationController(context),
+                mainExecutor,
+                bgExecutor,
+            )
+
+        val bubbleController =
+            createBubbleController(
+                bubbleData,
+                windowManager,
+                bubbleLogger,
+                bubblePositioner,
+                mainExecutor,
+                bgExecutor,
+            )
+        bubbleController.asBubbles().setSysuiProxy(mock(SysuiProxy::class.java))
+        // Flush so that proxy gets set
+        mainExecutor.flushAll()
+
+        bubbleBarLayerView = BubbleBarLayerView(context, bubbleController, bubbleData, bubbleLogger)
+
+        val expandedViewManager = createExpandedViewManager()
+        val bubbleTaskView = FakeBubbleTaskViewFactory(mainExecutor).create()
+        val bubbleBarExpandedView =
+            (LayoutInflater.from(context)
+                    .inflate(R.layout.bubble_bar_expanded_view, null, false /* attachToRoot */)
+                    as BubbleBarExpandedView)
+                .apply {
+                    initialize(
+                        expandedViewManager,
+                        bubblePositioner,
+                        bubbleLogger,
+                        false /* isOverflow */,
+                        bubbleTaskView,
+                        mainExecutor,
+                        bgExecutor,
+                        null, /* regionSamplingProvider */
+                    )
+                }
+
+        val viewInfo = FakeBubbleFactory.createViewInfo(bubbleBarExpandedView)
+        bubble = FakeBubbleFactory.createChatBubbleWithViewInfo(context, viewInfo = viewInfo)
+    }
+
+    private fun createBubbleController(
+        bubbleData: BubbleData,
+        windowManager: WindowManager?,
+        bubbleLogger: BubbleLogger,
+        bubblePositioner: BubblePositioner,
+        mainExecutor: TestExecutor,
+        bgExecutor: TestExecutor,
+    ): BubbleController {
+        val shellInit = ShellInit(mainExecutor)
+        val shellCommandHandler = ShellCommandHandler()
+        val shellController =
+            ShellController(
+                context,
+                shellInit,
+                shellCommandHandler,
+                mock<DisplayInsetsController>(),
+                mainExecutor,
+            )
+        val surfaceSynchronizer = { obj: Runnable -> obj.run() }
+
+        val bubbleDataRepository =
+            BubbleDataRepository(
+                mock<LauncherApps>(),
+                mainExecutor,
+                bgExecutor,
+                BubblePersistentRepository(context),
+            )
+
+        return BubbleController(
+            context,
+            shellInit,
+            shellCommandHandler,
+            shellController,
+            bubbleData,
+            surfaceSynchronizer,
+            FloatingContentCoordinator(),
+            bubbleDataRepository,
+            mock<IStatusBarService>(),
+            windowManager,
+            WindowManagerShellWrapper(mainExecutor),
+            mock<UserManager>(),
+            mock<LauncherApps>(),
+            bubbleLogger,
+            mock<TaskStackListenerImpl>(),
+            mock<ShellTaskOrganizer>(),
+            bubblePositioner,
+            mock<DisplayController>(),
+            null,
+            null,
+            mainExecutor,
+            mock<Handler>(),
+            bgExecutor,
+            mock<TaskViewTransitions>(),
+            mock<Transitions>(),
+            SyncTransactionQueue(TransactionPool(), mainExecutor),
+            mock<IWindowManager>(),
+            mock<BubbleProperties>(),
+        )
+    }
+
+    @Test
+    fun testEventLogging_dismissExpandedViewViaDrag() {
+        getInstrumentation().runOnMainSync { bubbleBarLayerView.showExpandedView(bubble) }
+        assertThat(bubbleBarLayerView.findViewById<View>(R.id.bubble_bar_handle_view)).isNotNull()
+
+        bubbleBarLayerView.dragController?.dragListener?.onReleased(true)
+
+        assertThat(uiEventLoggerFake.numLogs()).isEqualTo(1)
+        assertThat(uiEventLoggerFake.logs[0].eventId)
+            .isEqualTo(BubbleLogger.Event.BUBBLE_BAR_BUBBLE_DISMISSED_DRAG_EXP_VIEW.id)
+        assertThat(uiEventLoggerFake.logs[0]).hasBubbleInfo(bubble)
+    }
+
+    private inner class FakeBubbleTaskViewFactory(private val mainExecutor: ShellExecutor) :
+        BubbleTaskViewFactory {
+        override fun create(): BubbleTaskView {
+            val taskViewTaskController = mock<TaskViewTaskController>()
+            val taskView = TaskView(context, taskViewTaskController)
+            val taskInfo = mock<ActivityManager.RunningTaskInfo>()
+            whenever(taskViewTaskController.taskInfo).thenReturn(taskInfo)
+            return BubbleTaskView(taskView, mainExecutor)
+        }
+    }
+
+    private fun createExpandedViewManager(): BubbleExpandedViewManager {
+        return object : BubbleExpandedViewManager {
+            override val overflowBubbles: List<Bubble>
+                get() = Collections.emptyList()
+
+            override fun setOverflowListener(listener: BubbleData.Listener) {}
+
+            override fun collapseStack() {}
+
+            override fun updateWindowFlagsForBackpress(intercept: Boolean) {}
+
+            override fun promoteBubbleFromOverflow(bubble: Bubble) {}
+
+            override fun removeBubble(key: String, reason: Int) {}
+
+            override fun dismissBubble(bubble: Bubble, reason: Int) {}
+
+            override fun setAppBubbleTaskId(key: String, taskId: Int) {}
+
+            override fun isStackExpanded(): Boolean {
+                return true
+            }
+
+            override fun isShowingAsBubbleBar(): Boolean {
+                return true
+            }
+
+            override fun hideCurrentInputMethod() {}
+
+            override fun updateBubbleBarLocation(location: BubbleBarLocation) {}
+        }
+    }
+
+    private class TestExecutor : ShellExecutor {
+
+        private val runnables: MutableList<Runnable> = mutableListOf()
+
+        override fun execute(runnable: Runnable) {
+            runnables.add(runnable)
+        }
+
+        override fun executeDelayed(runnable: Runnable, delayMillis: Long) {
+            execute(runnable)
+        }
+
+        override fun removeCallbacks(runnable: Runnable?) {}
+
+        override fun hasCallback(runnable: Runnable?): Boolean = false
+
+        fun flushAll() {
+            while (runnables.isNotEmpty()) {
+                runnables.removeAt(0).run()
+            }
+        }
+    }
+}
diff --git a/libs/WindowManager/Shell/res/layout/bubble_bar_manage_education.xml b/libs/WindowManager/Shell/res/layout/bubble_bar_manage_education.xml
index 4a42616..4c7d1c7 100644
--- a/libs/WindowManager/Shell/res/layout/bubble_bar_manage_education.xml
+++ b/libs/WindowManager/Shell/res/layout/bubble_bar_manage_education.xml
@@ -22,7 +22,6 @@
     android:layout_gravity="center_horizontal"
     android:layout_marginTop="@dimen/bubble_popup_margin_top"
     android:layout_marginHorizontal="@dimen/bubble_popup_margin_horizontal"
-    android:layout_marginBottom="@dimen/bubble_popup_margin_bottom"
     android:elevation="@dimen/bubble_popup_elevation"
     android:gravity="center_horizontal"
     android:orientation="vertical">
@@ -30,7 +29,7 @@
     <ImageView
         android:layout_width="@dimen/bubble_popup_icon_size"
         android:layout_height="@dimen/bubble_popup_icon_size"
-        android:tint="?androidprv:attr/materialColorPrimary"
+        android:tint="?androidprv:attr/materialColorOutline"
         android:contentDescription="@null"
         android:src="@drawable/pip_ic_settings"/>
 
@@ -49,6 +48,7 @@
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:layout_marginTop="@dimen/bubble_popup_text_margin"
+        android:paddingBottom="@dimen/bubble_popup_padding_bottom"
         android:maxWidth="@dimen/bubble_popup_content_max_width"
         android:textAppearance="@android:style/TextAppearance.DeviceDefault"
         android:textColor="?androidprv:attr/materialColorOnSurfaceVariant"
diff --git a/libs/WindowManager/Shell/res/layout/bubble_bar_stack_education.xml b/libs/WindowManager/Shell/res/layout/bubble_bar_stack_education.xml
index f19c3c7..345c399 100644
--- a/libs/WindowManager/Shell/res/layout/bubble_bar_stack_education.xml
+++ b/libs/WindowManager/Shell/res/layout/bubble_bar_stack_education.xml
@@ -22,7 +22,6 @@
     android:layout_gravity="bottom|end"
     android:layout_marginTop="@dimen/bubble_popup_margin_top"
     android:layout_marginHorizontal="@dimen/bubble_popup_margin_horizontal"
-    android:layout_marginBottom="@dimen/bubble_popup_margin_bottom"
     android:elevation="@dimen/bubble_popup_elevation"
     android:gravity="center_horizontal"
     android:orientation="vertical">
@@ -49,6 +48,7 @@
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:layout_marginTop="@dimen/bubble_popup_text_margin"
+        android:paddingBottom="@dimen/bubble_popup_padding_bottom"
         android:maxWidth="@dimen/bubble_popup_content_max_width"
         android:textAppearance="@android:style/TextAppearance.DeviceDefault"
         android:textColor="?androidprv:attr/materialColorOnSurfaceVariant"
diff --git a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_maximize_menu.xml b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_maximize_menu.xml
index 3759618..5e41865 100644
--- a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_maximize_menu.xml
+++ b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_maximize_menu.xml
@@ -17,20 +17,53 @@
 <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
     android:id="@+id/maximize_menu"
-    android:layout_width="@dimen/desktop_mode_maximize_menu_width"
+    android:layout_width="wrap_content"
     android:layout_height="@dimen/desktop_mode_maximize_menu_height"
     android:background="@drawable/desktop_mode_maximize_menu_background"
     android:elevation="1dp">
 
     <LinearLayout
         android:id="@+id/container"
-        android:layout_width="@dimen/desktop_mode_maximize_menu_width"
+        android:layout_width="wrap_content"
         android:layout_height="@dimen/desktop_mode_maximize_menu_height"
         android:orientation="horizontal"
         android:padding="16dp"
         android:gravity="center">
 
         <LinearLayout
+            android:id="@+id/maximize_menu_immersive_toggle_container"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:orientation="vertical">
+
+            <Button
+                android:layout_width="94dp"
+                android:layout_height="60dp"
+                android:id="@+id/maximize_menu_immersive_toggle_button"
+                style="?android:attr/buttonBarButtonStyle"
+                android:stateListAnimator="@null"
+                android:importantForAccessibility="yes"
+                android:contentDescription="@string/desktop_mode_maximize_menu_immersive_button_text"
+                android:layout_marginEnd="8dp"
+                android:layout_marginBottom="4dp"
+                android:alpha="0"/>
+
+            <TextView
+                android:id="@+id/maximize_menu_immersive_toggle_button_text"
+                android:layout_width="94dp"
+                android:layout_height="18dp"
+                android:textSize="11sp"
+                android:layout_marginBottom="76dp"
+                android:gravity="center"
+                android:fontFamily="google-sans-text"
+                android:importantForAccessibility="no"
+                android:text="@string/desktop_mode_maximize_menu_immersive_button_text"
+                android:textColor="?androidprv:attr/materialColorOnSurface"
+                android:alpha="0"/>
+        </LinearLayout>
+
+        <LinearLayout
+            android:id="@+id/maximize_menu_size_toggle_container"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:orientation="vertical">
@@ -43,7 +76,6 @@
                 android:stateListAnimator="@null"
                 android:importantForAccessibility="yes"
                 android:contentDescription="@string/desktop_mode_maximize_menu_maximize_button_text"
-                android:layout_marginRight="8dp"
                 android:layout_marginBottom="4dp"
                 android:alpha="0"/>
 
@@ -62,6 +94,7 @@
         </LinearLayout>
 
         <LinearLayout
+            android:id="@+id/maximize_menu_snap_container"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:orientation="vertical">
@@ -73,6 +106,7 @@
                 android:padding="4dp"
                 android:background="@drawable/desktop_mode_maximize_menu_layout_background"
                 android:layout_marginBottom="4dp"
+                android:layout_marginStart="8dp"
                 android:alpha="0">
                 <Button
                     android:id="@+id/maximize_menu_snap_left_button"
@@ -115,7 +149,7 @@
      used to monitor input events over the entire menu. -->
     <View
         android:id="@+id/maximize_menu_overlay"
-        android:layout_width="@dimen/desktop_mode_maximize_menu_width"
+        android:layout_width="match_parent"
         android:layout_height="@dimen/desktop_mode_maximize_menu_height"/>
 </FrameLayout>
 
diff --git a/libs/WindowManager/Shell/res/layout/tiling_split_divider.xml b/libs/WindowManager/Shell/res/layout/tiling_split_divider.xml
new file mode 100644
index 0000000..ce2e8be
--- /dev/null
+++ b/libs/WindowManager/Shell/res/layout/tiling_split_divider.xml
@@ -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.
+  -->
+
+<com.android.wm.shell.windowdecor.tiling.TilingDividerView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_height="match_parent"
+    android:layout_width="match_parent"
+    android:id="@+id/divider_bar">
+
+        <com.android.wm.shell.common.split.DividerHandleView
+            android:id="@+id/docked_divider_handle"
+            android:layout_height="match_parent"
+            android:layout_width="match_parent"
+            android:layout_gravity="center"
+            android:contentDescription="@string/accessibility_divider"
+            android:background="@null"/>
+
+        <com.android.wm.shell.common.split.DividerRoundedCorner
+            android:id="@+id/docked_divider_rounded_corner"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"/>
+
+
+</com.android.wm.shell.windowdecor.tiling.TilingDividerView>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/values-af/strings.xml b/libs/WindowManager/Shell/res/values-af/strings.xml
index b29e8bf..95c2bb5 100644
--- a/libs/WindowManager/Shell/res/values-af/strings.xml
+++ b/libs/WindowManager/Shell/res/values-af/strings.xml
@@ -133,6 +133,10 @@
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maksimeer skerm"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Gryp skerm vas"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"App kan nie hierheen geskuif word nie"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maksimeer"</string>
     <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Stel terug"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Spring na links"</string>
diff --git a/libs/WindowManager/Shell/res/values-am/strings.xml b/libs/WindowManager/Shell/res/values-am/strings.xml
index d96033f..ba74e34 100644
--- a/libs/WindowManager/Shell/res/values-am/strings.xml
+++ b/libs/WindowManager/Shell/res/values-am/strings.xml
@@ -133,6 +133,10 @@
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"የማያ ገጹ መጠን አሳድግ"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"ማያ ገጹን አሳድግ"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"መተግበሪያ ወደዚህ መንቀሳቀስ አይችልም"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"አሳድግ"</string>
     <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"ወደነበረበት መልስ"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"ወደ ግራ አሳድግ"</string>
diff --git a/libs/WindowManager/Shell/res/values-ar/strings.xml b/libs/WindowManager/Shell/res/values-ar/strings.xml
index 42ef4c3..c36783f 100644
--- a/libs/WindowManager/Shell/res/values-ar/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ar/strings.xml
@@ -133,6 +133,10 @@
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"تكبير الشاشة إلى أقصى حدّ"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"التقاط صورة للشاشة"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"لا يمكن نقل التطبيق إلى هنا"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"تكبير"</string>
     <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"استعادة"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"المحاذاة إلى اليسار"</string>
diff --git a/libs/WindowManager/Shell/res/values-as/strings.xml b/libs/WindowManager/Shell/res/values-as/strings.xml
index 8e9c704..71d95ed 100644
--- a/libs/WindowManager/Shell/res/values-as/strings.xml
+++ b/libs/WindowManager/Shell/res/values-as/strings.xml
@@ -133,9 +133,12 @@
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"স্ক্ৰীন মেক্সিমাইজ কৰক"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"স্ক্ৰীন স্নেপ কৰক"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"ইয়ালৈ এপ্‌টো আনিব নোৱাৰি"</string>
-    <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"মেক্সিমাইজ কৰক"</string>
-    <!-- no translation found for desktop_mode_maximize_menu_restore_button_text (4234449220944704387) -->
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
     <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
+    <skip />
+    <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"মেক্সিমাইজ কৰক"</string>
+    <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"পুনঃস্থাপন কৰক"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"বাওঁফাললৈ স্নেপ কৰক"</string>
     <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"সোঁফাললৈ স্নেপ কৰক"</string>
     <string name="open_by_default_settings_text" msgid="2526548548598185500">"ডিফ’ল্ট ছেটিং খোলক"</string>
diff --git a/libs/WindowManager/Shell/res/values-az/strings.xml b/libs/WindowManager/Shell/res/values-az/strings.xml
index 1310f6f..aa232e3 100644
--- a/libs/WindowManager/Shell/res/values-az/strings.xml
+++ b/libs/WindowManager/Shell/res/values-az/strings.xml
@@ -133,6 +133,10 @@
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Ekranı maksimum böyüdün"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Ekranı çəkin"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Tətbiqi bura köçürmək mümkün deyil"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Böyüdün"</string>
     <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Bərpa edin"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Sola tərəf çəkin"</string>
diff --git a/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml b/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml
index b9c4239..256344a 100644
--- a/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml
@@ -133,6 +133,10 @@
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Povećaj ekran"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Uklopi ekran"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Aplikacija ne može da se premesti ovde"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Uvećajte"</string>
     <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Vratite"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Prikačite levo"</string>
diff --git a/libs/WindowManager/Shell/res/values-be/strings.xml b/libs/WindowManager/Shell/res/values-be/strings.xml
index 5d389d8..701c510 100644
--- a/libs/WindowManager/Shell/res/values-be/strings.xml
+++ b/libs/WindowManager/Shell/res/values-be/strings.xml
@@ -133,6 +133,10 @@
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Разгарнуць на ўвесь экран"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Размясціць на палавіне экрана"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Нельга перамясціць сюды праграму"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Разгарнуць"</string>
     <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Аднавіць"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Размясціць злева"</string>
diff --git a/libs/WindowManager/Shell/res/values-bg/strings.xml b/libs/WindowManager/Shell/res/values-bg/strings.xml
index 8078786..9ab86f4 100644
--- a/libs/WindowManager/Shell/res/values-bg/strings.xml
+++ b/libs/WindowManager/Shell/res/values-bg/strings.xml
@@ -133,6 +133,10 @@
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Увеличаване на екрана"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Прилепване на екрана"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Приложението не може да бъде преместено тук"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Увеличаване"</string>
     <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Възстановяване"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Прилепване наляво"</string>
diff --git a/libs/WindowManager/Shell/res/values-bn/strings.xml b/libs/WindowManager/Shell/res/values-bn/strings.xml
index 8db7144..4412497 100644
--- a/libs/WindowManager/Shell/res/values-bn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-bn/strings.xml
@@ -133,9 +133,12 @@
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"স্ক্রিন বড় করুন"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"স্ক্রিনে অ্যাপ মানানসই হিসেবে ছোট বড় করুন"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"অ্যাপটি এখানে সরানো যাবে না"</string>
-    <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"বড় করুন"</string>
-    <!-- no translation found for desktop_mode_maximize_menu_restore_button_text (4234449220944704387) -->
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
     <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
+    <skip />
+    <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"বড় করুন"</string>
+    <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"ফিরিয়ে আনুন"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"বাঁদিকে স্ন্যাপ করুন"</string>
     <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"ডানদিকে স্ন্যাপ করুন"</string>
     <string name="open_by_default_settings_text" msgid="2526548548598185500">"ডিফল্ট হিসেবে থাকা সেটিংস খুলুন"</string>
diff --git a/libs/WindowManager/Shell/res/values-bs/strings.xml b/libs/WindowManager/Shell/res/values-bs/strings.xml
index 3d922d8..674022b 100644
--- a/libs/WindowManager/Shell/res/values-bs/strings.xml
+++ b/libs/WindowManager/Shell/res/values-bs/strings.xml
@@ -133,6 +133,10 @@
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maksimiziraj ekran"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Snimi ekran"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Ne možete premjestiti aplikaciju ovdje"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maksimiziranje"</string>
     <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Vraćanje"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Pomicanje ulijevo"</string>
diff --git a/libs/WindowManager/Shell/res/values-ca/strings.xml b/libs/WindowManager/Shell/res/values-ca/strings.xml
index dc96cd0..499ed32 100644
--- a/libs/WindowManager/Shell/res/values-ca/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ca/strings.xml
@@ -133,6 +133,10 @@
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximitza la pantalla"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Ajusta la pantalla"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"L\'aplicació no es pot moure aquí"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maximitza"</string>
     <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Restaura"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Ajusta a l\'esquerra"</string>
diff --git a/libs/WindowManager/Shell/res/values-cs/strings.xml b/libs/WindowManager/Shell/res/values-cs/strings.xml
index 30ba78a..fc6b679 100644
--- a/libs/WindowManager/Shell/res/values-cs/strings.xml
+++ b/libs/WindowManager/Shell/res/values-cs/strings.xml
@@ -133,6 +133,10 @@
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximalizovat obrazovku"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Rozpůlit obrazovku"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Aplikaci sem nelze přesunout"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maximalizovat"</string>
     <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Obnovit"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Přichytit vlevo"</string>
diff --git a/libs/WindowManager/Shell/res/values-da/strings.xml b/libs/WindowManager/Shell/res/values-da/strings.xml
index 433b858..430cf96 100644
--- a/libs/WindowManager/Shell/res/values-da/strings.xml
+++ b/libs/WindowManager/Shell/res/values-da/strings.xml
@@ -133,6 +133,10 @@
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maksimér skærm"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Tilpas skærm"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Apps kan ikke flyttes hertil"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maksimér"</string>
     <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Gendan"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Fastgør til venstre"</string>
diff --git a/libs/WindowManager/Shell/res/values-de/strings.xml b/libs/WindowManager/Shell/res/values-de/strings.xml
index 0b4d189..cafaa89 100644
--- a/libs/WindowManager/Shell/res/values-de/strings.xml
+++ b/libs/WindowManager/Shell/res/values-de/strings.xml
@@ -133,9 +133,12 @@
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Bildschirm maximieren"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Bildschirm teilen"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Die App kann nicht hierher verschoben werden"</string>
-    <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maximieren"</string>
-    <!-- no translation found for desktop_mode_maximize_menu_restore_button_text (4234449220944704387) -->
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
     <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
+    <skip />
+    <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maximieren"</string>
+    <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Wiederherstellen"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Links andocken"</string>
     <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Rechts andocken"</string>
     <string name="open_by_default_settings_text" msgid="2526548548598185500">"Einstellungen für die Option „Standardmäßig öffnen“"</string>
diff --git a/libs/WindowManager/Shell/res/values-el/strings.xml b/libs/WindowManager/Shell/res/values-el/strings.xml
index beff019..d06b46f 100644
--- a/libs/WindowManager/Shell/res/values-el/strings.xml
+++ b/libs/WindowManager/Shell/res/values-el/strings.xml
@@ -133,6 +133,10 @@
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Μεγιστοποίηση οθόνης"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Προβολή στο μισό της οθόνης"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Δεν είναι δυνατή η μετακίνηση της εφαρμογής εδώ"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Μεγιστοποίηση"</string>
     <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Επαναφορά"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Κούμπωμα αριστερά"</string>
diff --git a/libs/WindowManager/Shell/res/values-en-rAU/strings.xml b/libs/WindowManager/Shell/res/values-en-rAU/strings.xml
index 01d3d25..c1a42448 100644
--- a/libs/WindowManager/Shell/res/values-en-rAU/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rAU/strings.xml
@@ -133,6 +133,10 @@
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximise screen"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Snap screen"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"App can\'t be moved here"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maximise"</string>
     <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Restore"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Snap left"</string>
diff --git a/libs/WindowManager/Shell/res/values-en-rCA/strings.xml b/libs/WindowManager/Shell/res/values-en-rCA/strings.xml
index 20ec076..2d123ec 100644
--- a/libs/WindowManager/Shell/res/values-en-rCA/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rCA/strings.xml
@@ -133,6 +133,8 @@
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximize Screen"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Snap Screen"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"App can\'t be moved here"</string>
+    <string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"Immersive"</string>
+    <string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"Restore"</string>
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maximize"</string>
     <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Restore"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Snap left"</string>
diff --git a/libs/WindowManager/Shell/res/values-en-rGB/strings.xml b/libs/WindowManager/Shell/res/values-en-rGB/strings.xml
index 01d3d25..c1a42448 100644
--- a/libs/WindowManager/Shell/res/values-en-rGB/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rGB/strings.xml
@@ -133,6 +133,10 @@
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximise screen"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Snap screen"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"App can\'t be moved here"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maximise"</string>
     <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Restore"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Snap left"</string>
diff --git a/libs/WindowManager/Shell/res/values-en-rIN/strings.xml b/libs/WindowManager/Shell/res/values-en-rIN/strings.xml
index 01d3d25..c1a42448 100644
--- a/libs/WindowManager/Shell/res/values-en-rIN/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rIN/strings.xml
@@ -133,6 +133,10 @@
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximise screen"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Snap screen"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"App can\'t be moved here"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maximise"</string>
     <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Restore"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Snap left"</string>
diff --git a/libs/WindowManager/Shell/res/values-es-rUS/strings.xml b/libs/WindowManager/Shell/res/values-es-rUS/strings.xml
index c9e7ecb..210b708 100644
--- a/libs/WindowManager/Shell/res/values-es-rUS/strings.xml
+++ b/libs/WindowManager/Shell/res/values-es-rUS/strings.xml
@@ -133,6 +133,10 @@
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximizar pantalla"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Ajustar pantalla"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"No se puede mover la app aquí"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maximizar"</string>
     <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Restablecer"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Ajustar a la izquierda"</string>
diff --git a/libs/WindowManager/Shell/res/values-es/strings.xml b/libs/WindowManager/Shell/res/values-es/strings.xml
index 903294b..3c7bfe5 100644
--- a/libs/WindowManager/Shell/res/values-es/strings.xml
+++ b/libs/WindowManager/Shell/res/values-es/strings.xml
@@ -133,6 +133,10 @@
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximizar pantalla"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Ajustar pantalla"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"La aplicación no se puede mover aquí"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maximizar"</string>
     <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Restaurar"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Acoplar a la izquierda"</string>
diff --git a/libs/WindowManager/Shell/res/values-et/strings.xml b/libs/WindowManager/Shell/res/values-et/strings.xml
index f6bebff..d17ee02 100644
--- a/libs/WindowManager/Shell/res/values-et/strings.xml
+++ b/libs/WindowManager/Shell/res/values-et/strings.xml
@@ -133,6 +133,10 @@
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Kuva täisekraanil"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Kuva poolel ekraanil"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Rakendust ei saa siia teisaldada"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maksimeeri"</string>
     <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Taasta"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Tõmmake vasakule"</string>
diff --git a/libs/WindowManager/Shell/res/values-eu/strings.xml b/libs/WindowManager/Shell/res/values-eu/strings.xml
index 267452f..f9419bc 100644
--- a/libs/WindowManager/Shell/res/values-eu/strings.xml
+++ b/libs/WindowManager/Shell/res/values-eu/strings.xml
@@ -133,6 +133,10 @@
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Handitu pantaila"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Zatitu pantaila"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Aplikazioa ezin da hona ekarri"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maximizatu"</string>
     <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Leheneratu"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Ezarri ezkerrean"</string>
diff --git a/libs/WindowManager/Shell/res/values-fa/strings.xml b/libs/WindowManager/Shell/res/values-fa/strings.xml
index ec4779b..a3d3cbc 100644
--- a/libs/WindowManager/Shell/res/values-fa/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fa/strings.xml
@@ -133,6 +133,10 @@
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"بزرگ کردن صفحه"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"بزرگ کردن صفحه"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"برنامه را نمی‌توان به اینجا منتقل کرد"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"بزرگ کردن"</string>
     <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"بازیابی کردن"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"کشیدن به‌چپ"</string>
diff --git a/libs/WindowManager/Shell/res/values-fi/strings.xml b/libs/WindowManager/Shell/res/values-fi/strings.xml
index 6dcd20c..ee5dd65 100644
--- a/libs/WindowManager/Shell/res/values-fi/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fi/strings.xml
@@ -133,6 +133,10 @@
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Suurenna näyttö"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Jaa näyttö"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Sovellusta ei voi siirtää tänne"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Suurenna"</string>
     <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Palauta"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Siirrä vasemmalle"</string>
diff --git a/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml b/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml
index df6d503..dc47891 100644
--- a/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml
@@ -133,6 +133,10 @@
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Agrandir l\'écran"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Aligner l\'écran"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Impossible de déplacer l\'appli ici"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Agrandir"</string>
     <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Restaurer"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Épingler à gauche"</string>
diff --git a/libs/WindowManager/Shell/res/values-fr/strings.xml b/libs/WindowManager/Shell/res/values-fr/strings.xml
index 3a7f2f0..a52ab49 100644
--- a/libs/WindowManager/Shell/res/values-fr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fr/strings.xml
@@ -133,6 +133,10 @@
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Mettre en plein écran"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Fractionner l\'écran"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Impossible de déplacer l\'appli ici"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Agrandir"</string>
     <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Restaurer"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Ancrer à gauche"</string>
diff --git a/libs/WindowManager/Shell/res/values-gl/strings.xml b/libs/WindowManager/Shell/res/values-gl/strings.xml
index cb7bb32..97d5e51 100644
--- a/libs/WindowManager/Shell/res/values-gl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-gl/strings.xml
@@ -133,6 +133,10 @@
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximizar pantalla"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Encaixar pantalla"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Non se pode mover aquí a aplicación"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maximizar"</string>
     <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Restaurar"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Axustar á esquerda"</string>
diff --git a/libs/WindowManager/Shell/res/values-gu/strings.xml b/libs/WindowManager/Shell/res/values-gu/strings.xml
index bee06fe..362ff8d 100644
--- a/libs/WindowManager/Shell/res/values-gu/strings.xml
+++ b/libs/WindowManager/Shell/res/values-gu/strings.xml
@@ -133,9 +133,12 @@
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"સ્ક્રીન કરો મોટી કરો"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"સ્ક્રીન સ્નૅપ કરો"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"ઍપ અહીં ખસેડી શકાતી નથી"</string>
-    <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"મોટું કરો"</string>
-    <!-- no translation found for desktop_mode_maximize_menu_restore_button_text (4234449220944704387) -->
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
     <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
+    <skip />
+    <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"મોટું કરો"</string>
+    <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"રિસ્ટોર કરો"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"ડાબે સ્નૅપ કરો"</string>
     <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"જમણે સ્નૅપ કરો"</string>
     <string name="open_by_default_settings_text" msgid="2526548548598185500">"\'ડિફૉલ્ટ તરીકે ખોલો\' સેટિંગ"</string>
diff --git a/libs/WindowManager/Shell/res/values-hi/strings.xml b/libs/WindowManager/Shell/res/values-hi/strings.xml
index 2c14199..ff675a9 100644
--- a/libs/WindowManager/Shell/res/values-hi/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hi/strings.xml
@@ -133,6 +133,10 @@
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"स्क्रीन को बड़ा करें"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"स्नैप स्क्रीन"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"ऐप्लिकेशन को यहां मूव नहीं किया जा सकता"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"बड़ा करें"</string>
     <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"पहले जैसा करें"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"बाईं ओर स्नैप करें"</string>
diff --git a/libs/WindowManager/Shell/res/values-hr/strings.xml b/libs/WindowManager/Shell/res/values-hr/strings.xml
index a2a52d1..16fbcd0 100644
--- a/libs/WindowManager/Shell/res/values-hr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hr/strings.xml
@@ -133,6 +133,10 @@
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maksimalno povećaj zaslon"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Izradi snimku zaslona"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Aplikacija se ne može premjestiti ovdje"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maksimiziraj"</string>
     <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Vrati"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Poravnaj lijevo"</string>
diff --git a/libs/WindowManager/Shell/res/values-hu/strings.xml b/libs/WindowManager/Shell/res/values-hu/strings.xml
index 03cc9f5..943b5eb 100644
--- a/libs/WindowManager/Shell/res/values-hu/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hu/strings.xml
@@ -133,6 +133,10 @@
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Képernyő méretének maximalizálása"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Igazodás a képernyő adott részéhez"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Az alkalmazás nem helyezhető át ide"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Teljes méret"</string>
     <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Visszaállítás"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Balra igazítás"</string>
diff --git a/libs/WindowManager/Shell/res/values-hy/strings.xml b/libs/WindowManager/Shell/res/values-hy/strings.xml
index 28c762e..6bcfc9a 100644
--- a/libs/WindowManager/Shell/res/values-hy/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hy/strings.xml
@@ -133,6 +133,10 @@
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Ծավալել էկրանը"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Ծալել էկրանը"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Հավելվածը հնարավոր չէ տեղափոխել այստեղ"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Ծավալել"</string>
     <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Վերականգնել"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Ամրացնել ձախ կողմում"</string>
diff --git a/libs/WindowManager/Shell/res/values-in/strings.xml b/libs/WindowManager/Shell/res/values-in/strings.xml
index 4929b79..96a3ebc 100644
--- a/libs/WindowManager/Shell/res/values-in/strings.xml
+++ b/libs/WindowManager/Shell/res/values-in/strings.xml
@@ -133,6 +133,10 @@
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Perbesar Layar"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Gabungkan Layar"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Aplikasi tidak dapat dipindahkan ke sini"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maksimalkan"</string>
     <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Pulihkan"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Maksimalkan ke kiri"</string>
diff --git a/libs/WindowManager/Shell/res/values-is/strings.xml b/libs/WindowManager/Shell/res/values-is/strings.xml
index d7ba53f..f595765 100644
--- a/libs/WindowManager/Shell/res/values-is/strings.xml
+++ b/libs/WindowManager/Shell/res/values-is/strings.xml
@@ -133,6 +133,10 @@
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Stækka skjá"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Smelluskjár"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Ekki er hægt að færa forritið hingað"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Stækka"</string>
     <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Endurheimta"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Smella til vinstri"</string>
diff --git a/libs/WindowManager/Shell/res/values-it/strings.xml b/libs/WindowManager/Shell/res/values-it/strings.xml
index 4522d37..8b7c15f 100644
--- a/libs/WindowManager/Shell/res/values-it/strings.xml
+++ b/libs/WindowManager/Shell/res/values-it/strings.xml
@@ -133,6 +133,10 @@
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Massimizza schermo"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Aggancia schermo"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Impossibile spostare l\'app qui"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Ingrandisci"</string>
     <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Ripristina"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Aggancia a sinistra"</string>
diff --git a/libs/WindowManager/Shell/res/values-iw/strings.xml b/libs/WindowManager/Shell/res/values-iw/strings.xml
index 110d357..17ffe8e 100644
--- a/libs/WindowManager/Shell/res/values-iw/strings.xml
+++ b/libs/WindowManager/Shell/res/values-iw/strings.xml
@@ -133,12 +133,16 @@
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"הגדלת המסך"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"כיווץ המסך"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"לא ניתן להעביר את האפליקציה לכאן"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"הגדלה"</string>
     <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"שחזור"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"הצמדה לשמאל"</string>
     <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"הצמדה לימין"</string>
     <string name="open_by_default_settings_text" msgid="2526548548598185500">"הגדרות לפתיחה כברירת מחדל"</string>
-    <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"בחירת האופן שבו קישורים לדפי אינטרנט אחרים ייפתחו באפליקציה הזו"</string>
+    <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"כאן בוחרים איך לפתוח באפליקציה הזו קישורים לדפי אינטרנט"</string>
     <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"באפליקציה"</string>
     <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"בדפדפן"</string>
     <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"אישור"</string>
diff --git a/libs/WindowManager/Shell/res/values-ja/strings.xml b/libs/WindowManager/Shell/res/values-ja/strings.xml
index 69421da..5464cfa 100644
--- a/libs/WindowManager/Shell/res/values-ja/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ja/strings.xml
@@ -133,6 +133,10 @@
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"画面の最大化"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"画面のスナップ"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"アプリはここに移動できません"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"最大化"</string>
     <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"復元"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"左にスナップ"</string>
diff --git a/libs/WindowManager/Shell/res/values-ka/strings.xml b/libs/WindowManager/Shell/res/values-ka/strings.xml
index 4685988..7ea9daf 100644
--- a/libs/WindowManager/Shell/res/values-ka/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ka/strings.xml
@@ -133,6 +133,10 @@
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"აპლიკაციის გაშლა სრულ ეკრანზე"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"აპლიკაციის დაპატარავება ეკრანზე"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"აპის აქ გადატანა შეუძლებელია"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"მაქსიმალურად გაშლა"</string>
     <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"აღდგენა"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"მარცხნივ გადატანა"</string>
diff --git a/libs/WindowManager/Shell/res/values-kk/strings.xml b/libs/WindowManager/Shell/res/values-kk/strings.xml
index 84e7ea5..45f85b9 100644
--- a/libs/WindowManager/Shell/res/values-kk/strings.xml
+++ b/libs/WindowManager/Shell/res/values-kk/strings.xml
@@ -133,6 +133,10 @@
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Экранды ұлғайту"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Экранды бөлу"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Қолданба бұл жерге қойылмайды."</string>
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Жаю"</string>
     <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Қалпына келтіру"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Солға тіркеу"</string>
diff --git a/libs/WindowManager/Shell/res/values-km/strings.xml b/libs/WindowManager/Shell/res/values-km/strings.xml
index 7eb81e1..83bd374 100644
--- a/libs/WindowManager/Shell/res/values-km/strings.xml
+++ b/libs/WindowManager/Shell/res/values-km/strings.xml
@@ -133,6 +133,10 @@
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"ពង្រីកអេក្រង់"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"ថតអេក្រង់"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"មិនអាចផ្លាស់ទីកម្មវិធីមកទីនេះបានទេ"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"ពង្រីក"</string>
     <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"ស្ដារ"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"ផ្លាស់ទីទៅឆ្វេង"</string>
diff --git a/libs/WindowManager/Shell/res/values-kn/strings.xml b/libs/WindowManager/Shell/res/values-kn/strings.xml
index 2b43f57..502e0c9 100644
--- a/libs/WindowManager/Shell/res/values-kn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-kn/strings.xml
@@ -133,6 +133,10 @@
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"ಸ್ಕ್ರೀನ್ ಅನ್ನು ಮ್ಯಾಕ್ಸಿಮೈಸ್ ಮಾಡಿ"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"ಸ್ನ್ಯಾಪ್ ಸ್ಕ್ರೀನ್"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"ಆ್ಯಪ್ ಅನ್ನು ಇಲ್ಲಿಗೆ ಸರಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"ಮ್ಯಾಕ್ಸಿಮೈಸ್ ಮಾಡಿ"</string>
     <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"ಮರುಸ್ಥಾಪಿಸಿ"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"ಎಡಕ್ಕೆ ಸ್ನ್ಯಾಪ್ ಮಾಡಿ"</string>
diff --git a/libs/WindowManager/Shell/res/values-ko/strings.xml b/libs/WindowManager/Shell/res/values-ko/strings.xml
index 8f36aab..2bf1b05 100644
--- a/libs/WindowManager/Shell/res/values-ko/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ko/strings.xml
@@ -133,6 +133,10 @@
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"화면 최대화"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"화면 분할"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"앱을 여기로 이동할 수 없음"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"최대화하기"</string>
     <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"복원"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"왼쪽으로 맞추기"</string>
diff --git a/libs/WindowManager/Shell/res/values-ky/strings.xml b/libs/WindowManager/Shell/res/values-ky/strings.xml
index c1219ba..392ae4c 100644
--- a/libs/WindowManager/Shell/res/values-ky/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ky/strings.xml
@@ -133,12 +133,16 @@
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Экранды чоңойтуу"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Экранды сүрөткө тартып алуу"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Колдонмону бул жерге жылдырууга болбойт"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Чоңойтуу"</string>
     <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Калыбына келтирүү"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Солго жылдыруу"</string>
     <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Оңго жылдыруу"</string>
-    <string name="open_by_default_settings_text" msgid="2526548548598185500">"Демейки шартта ачуу параметрлери"</string>
-    <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Колдонмодо шилтемелер кантип ачылышы керек экенин тандаңыз"</string>
+    <string name="open_by_default_settings_text" msgid="2526548548598185500">"Демейки шартта ачылуучу шилтемелердин параметрлери"</string>
+    <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Колдонмодо шилтемелер кантип ачыларын тандаңыз"</string>
     <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"Колдонмодо"</string>
     <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"Серепчиңизде"</string>
     <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"Жарайт"</string>
diff --git a/libs/WindowManager/Shell/res/values-lo/strings.xml b/libs/WindowManager/Shell/res/values-lo/strings.xml
index 375fc64..4e4b678 100644
--- a/libs/WindowManager/Shell/res/values-lo/strings.xml
+++ b/libs/WindowManager/Shell/res/values-lo/strings.xml
@@ -133,9 +133,12 @@
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"ປັບຈໍໃຫຍ່ສຸດ"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"ສະແນັບໜ້າຈໍ"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"ບໍ່ສາມາດຍ້າຍແອັບມາບ່ອນນີ້ໄດ້"</string>
-    <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"ຂະຫຍາຍໃຫຍ່ສຸດ"</string>
-    <!-- no translation found for desktop_mode_maximize_menu_restore_button_text (4234449220944704387) -->
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
     <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
+    <skip />
+    <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"ຂະຫຍາຍໃຫຍ່ສຸດ"</string>
+    <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"ກູ້ຄືນ"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"ແນບຊ້າຍ"</string>
     <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"ແນບຂວາ"</string>
     <string name="open_by_default_settings_text" msgid="2526548548598185500">"ເປີດຕາມການຕັ້ງຄ່າເລີ່ມຕົ້ນ"</string>
diff --git a/libs/WindowManager/Shell/res/values-lt/strings.xml b/libs/WindowManager/Shell/res/values-lt/strings.xml
index dfc3b45..4551f72 100644
--- a/libs/WindowManager/Shell/res/values-lt/strings.xml
+++ b/libs/WindowManager/Shell/res/values-lt/strings.xml
@@ -133,6 +133,10 @@
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Išskleisti ekraną"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Sutraukti ekraną"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Programos negalima perkelti čia"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Padidinti"</string>
     <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Atkurti"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Pritraukti kairėje"</string>
diff --git a/libs/WindowManager/Shell/res/values-lv/strings.xml b/libs/WindowManager/Shell/res/values-lv/strings.xml
index 8781852..60912f62 100644
--- a/libs/WindowManager/Shell/res/values-lv/strings.xml
+++ b/libs/WindowManager/Shell/res/values-lv/strings.xml
@@ -133,6 +133,10 @@
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maksimizēt ekrānu"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Fiksēt ekrānu"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Lietotni nevar pārvietot šeit."</string>
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maksimizēt"</string>
     <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Atjaunot"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Piestiprināt pa kreisi"</string>
diff --git a/libs/WindowManager/Shell/res/values-mk/strings.xml b/libs/WindowManager/Shell/res/values-mk/strings.xml
index 88fed74..7c0c856 100644
--- a/libs/WindowManager/Shell/res/values-mk/strings.xml
+++ b/libs/WindowManager/Shell/res/values-mk/strings.xml
@@ -133,6 +133,10 @@
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Максимизирај го екранот"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Подели го екранот на половина"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Апликацијата не може да се премести овде"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Максимизирај"</string>
     <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Врати"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Фотографирај лево"</string>
diff --git a/libs/WindowManager/Shell/res/values-ml/strings.xml b/libs/WindowManager/Shell/res/values-ml/strings.xml
index 71fb78e..e14ab8b 100644
--- a/libs/WindowManager/Shell/res/values-ml/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ml/strings.xml
@@ -133,6 +133,10 @@
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"സ്‌ക്രീൻ വലുതാക്കുക"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"സ്‌ക്രീൻ സ്‌നാപ്പ് ചെയ്യുക"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"ആപ്പ് ഇവിടേക്ക് നീക്കാനാകില്ല"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"വലുതാക്കുക"</string>
     <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"പുനഃസ്ഥാപിക്കുക"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"ഇടതുവശത്തേക്ക് സ്‌നാപ്പ് ചെയ്യുക"</string>
diff --git a/libs/WindowManager/Shell/res/values-mn/strings.xml b/libs/WindowManager/Shell/res/values-mn/strings.xml
index 04f2f82..d406b99 100644
--- a/libs/WindowManager/Shell/res/values-mn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-mn/strings.xml
@@ -133,9 +133,12 @@
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Дэлгэцийг томруулах"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Дэлгэцийг таллах"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Аппыг ийш зөөх боломжгүй"</string>
-    <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Томруулах"</string>
-    <!-- no translation found for desktop_mode_maximize_menu_restore_button_text (4234449220944704387) -->
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
     <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
+    <skip />
+    <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Томруулах"</string>
+    <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Сэргээх"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Зүүн тийш зэрэгцүүлэх"</string>
     <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Баруун тийш зэрэгцүүлэх"</string>
     <string name="open_by_default_settings_text" msgid="2526548548598185500">"Өгөгдмөл тохиргоогоор нээх"</string>
diff --git a/libs/WindowManager/Shell/res/values-mr/strings.xml b/libs/WindowManager/Shell/res/values-mr/strings.xml
index be1df32..a2c610e 100644
--- a/libs/WindowManager/Shell/res/values-mr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-mr/strings.xml
@@ -133,6 +133,10 @@
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"स्क्रीन मोठी करा"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"स्क्रीन स्नॅप करा"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"अ‍ॅप इथे हलवू शकत नाही"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"मोठे करा"</string>
     <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"रिस्टोअर करा"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"डावीकडे स्नॅप करा"</string>
diff --git a/libs/WindowManager/Shell/res/values-ms/strings.xml b/libs/WindowManager/Shell/res/values-ms/strings.xml
index 04da886..2102a38 100644
--- a/libs/WindowManager/Shell/res/values-ms/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ms/strings.xml
@@ -133,6 +133,10 @@
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maksimumkan Skrin"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Tangkap Skrin"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Apl tidak boleh dialihkan ke sini"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maksimumkan"</string>
     <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Pulihkan"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Autojajar ke kiri"</string>
diff --git a/libs/WindowManager/Shell/res/values-my/strings.xml b/libs/WindowManager/Shell/res/values-my/strings.xml
index 915a7cd..ae34624 100644
--- a/libs/WindowManager/Shell/res/values-my/strings.xml
+++ b/libs/WindowManager/Shell/res/values-my/strings.xml
@@ -133,6 +133,10 @@
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"စခရင်ကို ချဲ့မည်"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"စခရင်ကို ချုံ့မည်"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"အက်ပ်ကို ဤနေရာသို့ ရွှေ့၍မရပါ"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"ချဲ့ရန်"</string>
     <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"ပြန်ပြောင်းရန်"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"ဘယ်တွင် ချဲ့ရန်"</string>
diff --git a/libs/WindowManager/Shell/res/values-nb/strings.xml b/libs/WindowManager/Shell/res/values-nb/strings.xml
index 8e5aee1..9270dc8 100644
--- a/libs/WindowManager/Shell/res/values-nb/strings.xml
+++ b/libs/WindowManager/Shell/res/values-nb/strings.xml
@@ -133,6 +133,10 @@
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maksimer skjermen"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Fest skjermen"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Appen kan ikke flyttes hit"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maksimer"</string>
     <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Gjenopprett"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Fest til venstre"</string>
diff --git a/libs/WindowManager/Shell/res/values-ne/strings.xml b/libs/WindowManager/Shell/res/values-ne/strings.xml
index 42f2336..e9a5306 100644
--- a/libs/WindowManager/Shell/res/values-ne/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ne/strings.xml
@@ -133,6 +133,10 @@
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"स्क्रिन ठुलो बनाउनुहोस्"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"स्क्रिन स्न्याप गर्नुहोस्"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"एप सारेर यहाँ ल्याउन सकिएन"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"ठुलो बनाउनुहोस्"</string>
     <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"रिस्टोर गर्नुहोस्"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"बायाँतिर स्न्याप गर्नुहोस्"</string>
diff --git a/libs/WindowManager/Shell/res/values-nl/strings.xml b/libs/WindowManager/Shell/res/values-nl/strings.xml
index d19a4d4..45305d6 100644
--- a/libs/WindowManager/Shell/res/values-nl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-nl/strings.xml
@@ -133,6 +133,10 @@
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Scherm maximaliseren"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Scherm halveren"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Kan de app niet hierheen verplaatsen"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maximaliseren"</string>
     <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Herstellen"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Links uitlijnen"</string>
diff --git a/libs/WindowManager/Shell/res/values-or/strings.xml b/libs/WindowManager/Shell/res/values-or/strings.xml
index f67a6b2..2d30441 100644
--- a/libs/WindowManager/Shell/res/values-or/strings.xml
+++ b/libs/WindowManager/Shell/res/values-or/strings.xml
@@ -133,9 +133,12 @@
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"ସ୍କ୍ରିନକୁ ବଡ଼ କରନ୍ତୁ"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"ସ୍କ୍ରିନକୁ ସ୍ନାପ କରନ୍ତୁ"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"ଆପକୁ ଏଠାକୁ ମୁଭ କରାଯାଇପାରିବ ନାହିଁ"</string>
-    <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"ବଡ଼ କରନ୍ତୁ"</string>
-    <!-- no translation found for desktop_mode_maximize_menu_restore_button_text (4234449220944704387) -->
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
     <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
+    <skip />
+    <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"ବଡ଼ କରନ୍ତୁ"</string>
+    <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"ରିଷ୍ଟୋର କରନ୍ତୁ"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"ବାମରେ ସ୍ନାପ କରନ୍ତୁ"</string>
     <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"ଡାହାଣରେ ସ୍ନାପ କରନ୍ତୁ"</string>
     <string name="open_by_default_settings_text" msgid="2526548548598185500">"ଡିଫଲ୍ଟ ସେଟିଂସକୁ ଖୋଲନ୍ତୁ"</string>
diff --git a/libs/WindowManager/Shell/res/values-pa/strings.xml b/libs/WindowManager/Shell/res/values-pa/strings.xml
index 76d59af..26ba461 100644
--- a/libs/WindowManager/Shell/res/values-pa/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pa/strings.xml
@@ -133,9 +133,12 @@
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"ਸਕ੍ਰੀਨ ਦਾ ਆਕਾਰ ਵਧਾਓ"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"ਸਕ੍ਰੀਨ ਨੂੰ ਸਨੈਪ ਕਰੋ"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"ਐਪ ਨੂੰ ਇੱਥੇ ਨਹੀਂ ਲਿਜਾਇਆ ਜਾ ਸਕਦਾ"</string>
-    <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"ਵੱਡਾ ਕਰੋ"</string>
-    <!-- no translation found for desktop_mode_maximize_menu_restore_button_text (4234449220944704387) -->
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
     <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
+    <skip />
+    <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"ਵੱਡਾ ਕਰੋ"</string>
+    <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"ਮੁੜ-ਬਹਾਲ ਕਰੋ"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"ਖੱਬੇ ਪਾਸੇ ਸਨੈਪ ਕਰੋ"</string>
     <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"ਸੱਜੇ ਪਾਸੇ ਸਨੈਪ ਕਰੋ"</string>
     <string name="open_by_default_settings_text" msgid="2526548548598185500">"ਪੂਰਵ-ਨਿਰਧਾਰਿਤ ਸੈਟਿੰਗਾਂ ਮੁਤਾਬਕ ਖੋਲ੍ਹੋ"</string>
diff --git a/libs/WindowManager/Shell/res/values-pl/strings.xml b/libs/WindowManager/Shell/res/values-pl/strings.xml
index 502e53ab..14b7a09 100644
--- a/libs/WindowManager/Shell/res/values-pl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pl/strings.xml
@@ -133,6 +133,10 @@
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maksymalizuj ekran"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Przyciągnij ekran"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Nie można przenieść aplikacji tutaj"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maksymalizuj"</string>
     <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Przywróć"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Przyciągnij do lewej"</string>
diff --git a/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml b/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml
index 3ec5e76..8c7f9e7 100644
--- a/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml
@@ -133,6 +133,10 @@
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Ampliar tela"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Ajustar tela"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Não é possível mover o app para cá"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maximizar"</string>
     <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Restaurar"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Ajustar à esquerda"</string>
diff --git a/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml b/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml
index 184a5b3..9631c45 100644
--- a/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml
@@ -133,6 +133,10 @@
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximizar ecrã"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Encaixar ecrã"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Não é possível mover a app para aqui"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maximizar"</string>
     <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Restaurar"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Encaixar à esquerda"</string>
diff --git a/libs/WindowManager/Shell/res/values-pt/strings.xml b/libs/WindowManager/Shell/res/values-pt/strings.xml
index 3ec5e76..8c7f9e7 100644
--- a/libs/WindowManager/Shell/res/values-pt/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pt/strings.xml
@@ -133,6 +133,10 @@
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Ampliar tela"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Ajustar tela"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Não é possível mover o app para cá"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maximizar"</string>
     <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Restaurar"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Ajustar à esquerda"</string>
diff --git a/libs/WindowManager/Shell/res/values-ro/strings.xml b/libs/WindowManager/Shell/res/values-ro/strings.xml
index 9703328..e3fe280 100644
--- a/libs/WindowManager/Shell/res/values-ro/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ro/strings.xml
@@ -133,6 +133,10 @@
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximizează fereastra"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Micșorează fereastra și fixeaz-o"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Aplicația nu poate fi mutată aici"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maximizează"</string>
     <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Restabilește"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Trage la stânga"</string>
diff --git a/libs/WindowManager/Shell/res/values-ru/strings.xml b/libs/WindowManager/Shell/res/values-ru/strings.xml
index 401a8aab..442fca3 100644
--- a/libs/WindowManager/Shell/res/values-ru/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ru/strings.xml
@@ -133,6 +133,10 @@
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Развернуть на весь экран"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Свернуть"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Приложение нельзя сюда переместить"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Развернуть"</string>
     <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Восстановить"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Привязать слева"</string>
diff --git a/libs/WindowManager/Shell/res/values-si/strings.xml b/libs/WindowManager/Shell/res/values-si/strings.xml
index c101b4c..8a7ad3b 100644
--- a/libs/WindowManager/Shell/res/values-si/strings.xml
+++ b/libs/WindowManager/Shell/res/values-si/strings.xml
@@ -133,9 +133,12 @@
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"තිරය උපරිම කරන්න"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"ස්නැප් තිරය"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"යෙදුම මෙතැනට ගෙන යා නොහැක"</string>
-    <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"විහිදන්න"</string>
-    <!-- no translation found for desktop_mode_maximize_menu_restore_button_text (4234449220944704387) -->
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
     <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
+    <skip />
+    <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"විහිදන්න"</string>
+    <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"ප්‍රතිසාධනය කරන්න"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"වමට ස්නැප් කරන්න"</string>
     <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"දකුණට ස්නැප් කරන්න"</string>
     <string name="open_by_default_settings_text" msgid="2526548548598185500">"පෙරනිමි සැකසීම් මඟින් විවෘත කරන්න"</string>
diff --git a/libs/WindowManager/Shell/res/values-sk/strings.xml b/libs/WindowManager/Shell/res/values-sk/strings.xml
index 7214300..4234e80 100644
--- a/libs/WindowManager/Shell/res/values-sk/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sk/strings.xml
@@ -133,6 +133,10 @@
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximalizovať obrazovku"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Zobraziť polovicu obrazovky"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Aplikácia sa sem nedá presunúť"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maximalizovať"</string>
     <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Obnoviť"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Prichytiť vľavo"</string>
diff --git a/libs/WindowManager/Shell/res/values-sl/strings.xml b/libs/WindowManager/Shell/res/values-sl/strings.xml
index 04fe7e89..2012c8a 100644
--- a/libs/WindowManager/Shell/res/values-sl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sl/strings.xml
@@ -133,6 +133,10 @@
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maksimiraj zaslon"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Pripni zaslon"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Aplikacije ni mogoče premakniti sem"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maksimiraj"</string>
     <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Obnovi"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Pripni levo"</string>
diff --git a/libs/WindowManager/Shell/res/values-sq/strings.xml b/libs/WindowManager/Shell/res/values-sq/strings.xml
index 6662ca1a..de6f681 100644
--- a/libs/WindowManager/Shell/res/values-sq/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sq/strings.xml
@@ -133,9 +133,12 @@
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maksimizo ekranin"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Regjistro ekranin"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Aplikacioni nuk mund të zhvendoset këtu"</string>
-    <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maksimizo"</string>
-    <!-- no translation found for desktop_mode_maximize_menu_restore_button_text (4234449220944704387) -->
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
     <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
+    <skip />
+    <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maksimizo"</string>
+    <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Restauro"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Zhvendos majtas"</string>
     <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Zhvendos djathtas"</string>
     <string name="open_by_default_settings_text" msgid="2526548548598185500">"Hap sipas cilësimeve të parazgjedhura"</string>
diff --git a/libs/WindowManager/Shell/res/values-sr/strings.xml b/libs/WindowManager/Shell/res/values-sr/strings.xml
index d0a4ae6..901d6d9 100644
--- a/libs/WindowManager/Shell/res/values-sr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sr/strings.xml
@@ -133,6 +133,10 @@
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Повећај екран"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Уклопи екран"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Апликација не може да се премести овде"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Увећајте"</string>
     <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Вратите"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Прикачите лево"</string>
diff --git a/libs/WindowManager/Shell/res/values-sv/strings.xml b/libs/WindowManager/Shell/res/values-sv/strings.xml
index 6ce2a9a..6566801 100644
--- a/libs/WindowManager/Shell/res/values-sv/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sv/strings.xml
@@ -133,6 +133,10 @@
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximera skärmen"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Fäst skärmen"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Det går inte att flytta appen hit"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Utöka"</string>
     <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Återställ"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Fäst till vänster"</string>
diff --git a/libs/WindowManager/Shell/res/values-sw/strings.xml b/libs/WindowManager/Shell/res/values-sw/strings.xml
index 40967f0..a952011 100644
--- a/libs/WindowManager/Shell/res/values-sw/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sw/strings.xml
@@ -133,6 +133,10 @@
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Panua Dirisha kwenye Skrini"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Panga Madirisha kwenye Skrini"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Imeshindwa kuhamishia programu hapa"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Panua"</string>
     <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Rejesha"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Telezesha kushoto"</string>
diff --git a/libs/WindowManager/Shell/res/values-ta/strings.xml b/libs/WindowManager/Shell/res/values-ta/strings.xml
index 3140c2c..c4243f7 100644
--- a/libs/WindowManager/Shell/res/values-ta/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ta/strings.xml
@@ -133,6 +133,10 @@
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"திரையைப் பெரிதாக்கு"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"திரையை ஸ்னாப் செய்"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"ஆப்ஸை இங்கே நகர்த்த முடியாது"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"பெரிதாக்கும்"</string>
     <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"மீட்டெடுக்கும்"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"இடதுபுறம் நகர்த்தும்"</string>
diff --git a/libs/WindowManager/Shell/res/values-te/strings.xml b/libs/WindowManager/Shell/res/values-te/strings.xml
index 62e62c7..47fc48d 100644
--- a/libs/WindowManager/Shell/res/values-te/strings.xml
+++ b/libs/WindowManager/Shell/res/values-te/strings.xml
@@ -133,6 +133,10 @@
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"స్క్రీన్ సైజ్‌ను పెంచండి"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"స్క్రీన్‌ను స్నాప్ చేయండి"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"యాప్‌ను ఇక్కడకి తరలించడం సాధ్యం కాదు"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"మ్యాగ్జిమైజ్ చేయండి"</string>
     <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"రీస్టోర్ చేయండి"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"ఎడమ వైపున స్నాప్ చేయండి"</string>
diff --git a/libs/WindowManager/Shell/res/values-th/strings.xml b/libs/WindowManager/Shell/res/values-th/strings.xml
index e6386a2..2608075 100644
--- a/libs/WindowManager/Shell/res/values-th/strings.xml
+++ b/libs/WindowManager/Shell/res/values-th/strings.xml
@@ -133,6 +133,10 @@
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"ขยายหน้าจอให้ใหญ่สุด"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"สแนปหน้าจอ"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"ย้ายแอปมาที่นี่ไม่ได้"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"ขยายใหญ่สุด"</string>
     <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"คืนค่า"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"จัดพอดีกับทางซ้าย"</string>
diff --git a/libs/WindowManager/Shell/res/values-tl/strings.xml b/libs/WindowManager/Shell/res/values-tl/strings.xml
index 176be33..94166ef 100644
--- a/libs/WindowManager/Shell/res/values-tl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-tl/strings.xml
@@ -133,6 +133,10 @@
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"I-maximize ang Screen"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"I-snap ang Screen"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Hindi mailipat dito ang app"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"I-maximize"</string>
     <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"I-restore"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"I-snap pakaliwa"</string>
diff --git a/libs/WindowManager/Shell/res/values-tr/strings.xml b/libs/WindowManager/Shell/res/values-tr/strings.xml
index 73248e3..7eac4a8 100644
--- a/libs/WindowManager/Shell/res/values-tr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-tr/strings.xml
@@ -133,6 +133,10 @@
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Ekranı Büyüt"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Ekranın Yarısına Tuttur"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Uygulama buraya taşınamıyor"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Ekranı kapla"</string>
     <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Geri yükle"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Sola tuttur"</string>
diff --git a/libs/WindowManager/Shell/res/values-uk/strings.xml b/libs/WindowManager/Shell/res/values-uk/strings.xml
index a655a3eb..5fb14bf 100644
--- a/libs/WindowManager/Shell/res/values-uk/strings.xml
+++ b/libs/WindowManager/Shell/res/values-uk/strings.xml
@@ -133,6 +133,10 @@
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Розгорнути екран"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Зафіксувати екран"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Сюди не можна перемістити додаток"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Розгорнути"</string>
     <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Відновити"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Закріпити ліворуч"</string>
diff --git a/libs/WindowManager/Shell/res/values-ur/strings.xml b/libs/WindowManager/Shell/res/values-ur/strings.xml
index 4bdea83..bb0358f 100644
--- a/libs/WindowManager/Shell/res/values-ur/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ur/strings.xml
@@ -133,6 +133,10 @@
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"اسکرین کو بڑا کریں"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"اسکرین کا اسناپ شاٹ لیں"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"ایپ کو یہاں منتقل نہیں کیا جا سکتا"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"بڑا کریں"</string>
     <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"بحال کریں"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"دائیں منتقل کریں"</string>
diff --git a/libs/WindowManager/Shell/res/values-uz/strings.xml b/libs/WindowManager/Shell/res/values-uz/strings.xml
index b3a496f..0648dd1 100644
--- a/libs/WindowManager/Shell/res/values-uz/strings.xml
+++ b/libs/WindowManager/Shell/res/values-uz/strings.xml
@@ -133,6 +133,10 @@
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Ekranni yoyish"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Ekranni biriktirish"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Ilova bu yerga surilmaydi"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Yoyish"</string>
     <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Tiklash"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Chapga tortish"</string>
diff --git a/libs/WindowManager/Shell/res/values-vi/strings.xml b/libs/WindowManager/Shell/res/values-vi/strings.xml
index 36d66e4..dda2225 100644
--- a/libs/WindowManager/Shell/res/values-vi/strings.xml
+++ b/libs/WindowManager/Shell/res/values-vi/strings.xml
@@ -133,6 +133,10 @@
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Mở rộng màn hình"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Điều chỉnh kích thước màn hình"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Không di chuyển được ứng dụng đến đây"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Phóng to tối đa"</string>
     <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Khôi phục"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Di chuyển nhanh sang trái"</string>
diff --git a/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml b/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml
index 64446cd..ad50a04 100644
--- a/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml
@@ -133,6 +133,10 @@
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"最大化屏幕"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"屏幕快照"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"无法将应用移至此处"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"最大化"</string>
     <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"恢复"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"贴靠左侧"</string>
diff --git a/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml b/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml
index 4970e8b..1d7fb4c 100644
--- a/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml
@@ -133,6 +133,10 @@
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"畫面最大化"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"貼齊畫面"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"應用程式無法移至這裡"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"最大化"</string>
     <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"還原"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"貼齊左邊"</string>
diff --git a/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml b/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml
index fcdccca..8083e37 100644
--- a/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml
@@ -133,6 +133,10 @@
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"畫面最大化"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"貼齊畫面"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"應用程式無法移至此處"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"最大化"</string>
     <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"還原"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"靠左對齊"</string>
diff --git a/libs/WindowManager/Shell/res/values-zu/strings.xml b/libs/WindowManager/Shell/res/values-zu/strings.xml
index cbc6c02..092efd6 100644
--- a/libs/WindowManager/Shell/res/values-zu/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zu/strings.xml
@@ -133,6 +133,10 @@
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Khulisa Isikrini Sifike Ekugcineni"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Thwebula Isikrini"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"I-app ayikwazi ukuhanjiswa lapha"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Khulisa"</string>
     <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Buyisela"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Chofoza kwesobunxele"</string>
diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml
index d02c77e..fa1aa19 100644
--- a/libs/WindowManager/Shell/res/values/dimen.xml
+++ b/libs/WindowManager/Shell/res/values/dimen.xml
@@ -239,11 +239,11 @@
     <!-- Max width for the bubble popup view. -->
     <dimen name="bubble_popup_content_max_width">300dp</dimen>
     <!-- Horizontal margin for the bubble popup view. -->
-    <dimen name="bubble_popup_margin_horizontal">32dp</dimen>
+    <dimen name="bubble_popup_margin_horizontal">24dp</dimen>
     <!-- Top margin for the bubble bar education views. -->
     <dimen name="bubble_popup_margin_top">24dp</dimen>
-    <!-- Bottom margin for the bubble bar education views. -->
-    <dimen name="bubble_popup_margin_bottom">32dp</dimen>
+    <!-- Bottom padding for the bubble bar education views. -->
+    <dimen name="bubble_popup_padding_bottom">8dp</dimen>
     <!-- Text margin for the bubble bar education views. -->
     <dimen name="bubble_popup_text_margin">16dp</dimen>
     <!-- Size of icons in the bubble bar education views. -->
@@ -479,8 +479,10 @@
     <!-- The default minimum allowed window height when resizing a window in desktop mode. -->
     <dimen name="desktop_mode_minimum_window_height">352dp</dimen>
 
-    <!-- The width of the maximize menu in desktop mode. -->
-    <dimen name="desktop_mode_maximize_menu_width">228dp</dimen>
+    <!-- The width of the maximize menu in desktop mode, depending on the number of options -->
+    <dimen name="desktop_mode_maximize_menu_width_one_options">126dp</dimen>
+    <dimen name="desktop_mode_maximize_menu_width_two_options">228dp</dimen>
+    <dimen name="desktop_mode_maximize_menu_width_three_options">330dp</dimen>
 
     <!-- The height of the maximize menu in desktop mode. -->
     <dimen name="desktop_mode_maximize_menu_height">114dp</dimen>
@@ -502,10 +504,14 @@
     <dimen name="desktop_mode_maximize_menu_buttons_fill_radius">4dp</dimen>
     <!-- The padding between the outline and fill of the maximize menu snap and maximize buttons. -->
     <dimen name="desktop_mode_maximize_menu_snap_and_maximize_buttons_fill_padding">4dp</dimen>
+    <!-- The padding between the outline and fill of the maximize menu snap and maximize buttons. -->
+    <dimen name="desktop_mode_maximize_menu_snap_and_maximize_buttons_fill_padding_bottom">8dp</dimen>
     <!-- The vertical padding between the outline and fill of the maximize menu restore button. -->
     <dimen name="desktop_mode_maximize_menu_restore_button_fill_vertical_padding">13dp</dimen>
     <!-- The horizontal padding between the outline and fill of the maximize menu restore button. -->
     <dimen name="desktop_mode_maximize_menu_restore_button_fill_horizontal_padding">21dp</dimen>
+    <!-- The padding between the outline and fill of the maximize menu immersive button. -->
+    <dimen name="desktop_mode_maximize_menu_immersive_button_fill_padding">4dp</dimen>
 
     <!-- The corner radius of the maximize menu. -->
     <dimen name="desktop_mode_maximize_menu_corner_radius">8dp</dimen>
diff --git a/libs/WindowManager/Shell/res/values/strings.xml b/libs/WindowManager/Shell/res/values/strings.xml
index afac9f6..ef0386a 100644
--- a/libs/WindowManager/Shell/res/values/strings.xml
+++ b/libs/WindowManager/Shell/res/values/strings.xml
@@ -317,7 +317,10 @@
     <string name="desktop_mode_maximize_menu_snap_text">Snap Screen</string>
     <!-- Snap resizing non-resizable string. -->
     <string name="desktop_mode_non_resizable_snap_text">App can\'t be moved here</string>
-    <!-- Accessibility text for the Maximize Menu's maximize button [CHAR LIMIT=NONE] -->
+    <!-- Accessibility text for the Maximize Menu's immersive button [CHAR LIMIT=NONE] -->
+    <string name="desktop_mode_maximize_menu_immersive_button_text">Immersive</string>
+    <!-- Accessibility text for the Maximize Menu's immersive restore button [CHAR LIMIT=NONE] -->
+    <string name="desktop_mode_maximize_menu_immersive_restore_button_text">Restore</string>
     <string name="desktop_mode_maximize_menu_maximize_button_text">Maximize</string>
     <!-- Accessibility text for the Maximize Menu's restore button [CHAR LIMIT=NONE] -->
     <string name="desktop_mode_maximize_menu_restore_button_text">Restore</string>
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/animation/MinimizeAnimator.kt b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/animation/MinimizeAnimator.kt
new file mode 100644
index 0000000..0586e26
--- /dev/null
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/animation/MinimizeAnimator.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.wm.shell.shared.animation
+
+import android.animation.Animator
+import android.animation.AnimatorSet
+import android.animation.ValueAnimator
+import android.util.DisplayMetrics
+import android.view.SurfaceControl.Transaction
+import android.view.animation.LinearInterpolator
+import android.view.animation.PathInterpolator
+import android.window.TransitionInfo.Change
+
+/** Creates minimization animation */
+object MinimizeAnimator {
+
+    private const val MINIMIZE_ANIM_ALPHA_DURATION_MS = 100L
+
+    private val STANDARD_ACCELERATE = PathInterpolator(0.3f, 0f, 1f, 1f)
+
+    private val minimizeBoundsAnimationDef =
+        WindowAnimator.BoundsAnimationParams(
+            durationMs = 200,
+            endOffsetYDp = 12f,
+            endScale = 0.97f,
+            interpolator = STANDARD_ACCELERATE,
+        )
+
+    @JvmStatic
+    fun create(
+        displayMetrics: DisplayMetrics,
+        change: Change,
+        transaction: Transaction,
+        onAnimFinish: (Animator) -> Unit,
+    ): Animator {
+        val boundsAnimator = WindowAnimator.createBoundsAnimator(
+            displayMetrics,
+            minimizeBoundsAnimationDef,
+            change,
+            transaction,
+        )
+        val alphaAnimator = ValueAnimator.ofFloat(1f, 0f).apply {
+            duration = MINIMIZE_ANIM_ALPHA_DURATION_MS
+            interpolator = LinearInterpolator()
+            addUpdateListener { animation ->
+                transaction.setAlpha(change.leash, animation.animatedValue as Float).apply()
+            }
+        }
+        val listener = object : Animator.AnimatorListener {
+            override fun onAnimationEnd(animator: Animator) = onAnimFinish(animator)
+            override fun onAnimationCancel(animator: Animator) = Unit
+            override fun onAnimationRepeat(animator: Animator) = Unit
+            override fun onAnimationStart(animator: Animator) = Unit
+        }
+        return AnimatorSet().apply {
+            playTogether(boundsAnimator, alphaAnimator)
+            addListener(listener)
+        }
+    }
+}
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/animation/WindowAnimator.kt b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/animation/WindowAnimator.kt
new file mode 100644
index 0000000..91d66ea
--- /dev/null
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/animation/WindowAnimator.kt
@@ -0,0 +1,116 @@
+/*
+ * 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.shared.animation
+
+import android.animation.PointFEvaluator
+import android.animation.ValueAnimator
+import android.graphics.PointF
+import android.graphics.Rect
+import android.util.DisplayMetrics
+import android.util.TypedValue
+import android.view.SurfaceControl
+import android.view.animation.Interpolator
+import android.window.TransitionInfo
+
+/** Creates animations that can be applied to windows/surfaces. */
+object WindowAnimator {
+
+    /** Parameters defining a window bounds animation. */
+    data class BoundsAnimationParams(
+        val durationMs: Long,
+        val startOffsetYDp: Float = 0f,
+        val endOffsetYDp: Float = 0f,
+        val startScale: Float = 1f,
+        val endScale: Float = 1f,
+        val interpolator: Interpolator,
+    )
+
+    /**
+     * Creates an animator to reposition and scale the bounds of the leash of the given change.
+     *
+     * @param displayMetrics the metrics of the display where the animation plays in
+     * @param boundsAnimDef the parameters for the animation itself (duration, scale, position)
+     * @param change the change to which the animation should be applied
+     * @param transaction the transaction to apply the animation to
+     */
+    fun createBoundsAnimator(
+        displayMetrics: DisplayMetrics,
+        boundsAnimDef: BoundsAnimationParams,
+        change: TransitionInfo.Change,
+        transaction: SurfaceControl.Transaction,
+    ): ValueAnimator {
+        val startPos =
+            getPosition(
+                displayMetrics,
+                change.endAbsBounds,
+                boundsAnimDef.startScale,
+                boundsAnimDef.startOffsetYDp,
+            )
+        val leash = change.leash
+        val endPos =
+            getPosition(
+                displayMetrics,
+                change.endAbsBounds,
+                boundsAnimDef.endScale,
+                boundsAnimDef.endOffsetYDp,
+            )
+        return ValueAnimator.ofObject(PointFEvaluator(), startPos, endPos).apply {
+            duration = boundsAnimDef.durationMs
+            interpolator = boundsAnimDef.interpolator
+            addUpdateListener { animation ->
+                val animPos = animation.animatedValue as PointF
+                val animScale =
+                    interpolate(
+                        boundsAnimDef.startScale,
+                        boundsAnimDef.endScale,
+                        animation.animatedFraction
+                    )
+                transaction
+                    .setPosition(leash, animPos.x, animPos.y)
+                    .setScale(leash, animScale, animScale)
+                    .apply()
+            }
+        }
+    }
+
+    private fun interpolate(startVal: Float, endVal: Float, fraction: Float): Float {
+        require(fraction in 0.0f..1.0f)
+        return startVal + (endVal - startVal) * fraction
+    }
+
+    private fun getPosition(
+        displayMetrics: DisplayMetrics,
+        bounds: Rect,
+        scale: Float,
+        offsetYDp: Float
+    ) = PointF(bounds.left.toFloat(), bounds.top.toFloat()).apply {
+            check(scale in 0.0f..1.0f)
+            // Scale the bounds down with an anchor in the center
+            offset(
+                (bounds.width().toFloat() * (1 - scale) / 2),
+                (bounds.height().toFloat() * (1 - scale) / 2),
+            )
+            val offsetYPx =
+                TypedValue.applyDimension(
+                        TypedValue.COMPLEX_UNIT_DIP,
+                        offsetYDp,
+                        displayMetrics,
+                    )
+                    .toInt()
+            offset(/* dx= */ 0f, offsetYPx.toFloat())
+        }
+}
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeStatus.java b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeStatus.java
index 0150bcd..6bc995f1 100644
--- a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeStatus.java
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeStatus.java
@@ -102,6 +102,15 @@
             "persist.wm.debug.enter_desktop_by_default_on_freeform_display";
 
     /**
+     * Sysprop declaring whether to enable drag-to-maximize for desktop windows.
+     *
+     * <p>If it is not defined, then {@code R.integer.config_dragToMaximizeInDesktopMode}
+     * is used.
+     */
+    public static final String ENABLE_DRAG_TO_MAXIMIZE_SYS_PROP =
+            "persist.wm.debug.enable_drag_to_maximize";
+
+    /**
      * Sysprop declaring the maximum number of Tasks to show in Desktop Mode at any one time.
      *
      * <p>If it is not defined, then {@code R.integer.config_maxDesktopWindowingActiveTasks} is
@@ -230,6 +239,18 @@
                         R.bool.config_enterDesktopByDefaultOnFreeformDisplay));
     }
 
+    /**
+     * Return {@code true} if a window should be maximized when it's dragged to the top edge of the
+     * screen.
+     */
+    public static boolean shouldMaximizeWhenDragToTopEdge(@NonNull Context context) {
+        if (!Flags.enableDragToMaximize()) {
+            return false;
+        }
+        return SystemProperties.getBoolean(ENABLE_DRAG_TO_MAXIMIZE_SYS_PROP,
+                context.getResources().getBoolean(R.bool.config_dragToMaximizeInDesktopMode));
+    }
+
     /** Dumps DesktopModeStatus flags and configs. */
     public static void dump(PrintWriter pw, String prefix, Context context) {
         String innerPrefix = prefix + "  ";
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/ManageWindowsViewContainer.kt b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/ManageWindowsViewContainer.kt
index 0e8e904..23e7441 100644
--- a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/ManageWindowsViewContainer.kt
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/ManageWindowsViewContainer.kt
@@ -15,6 +15,10 @@
  */
 
 package com.android.wm.shell.shared.desktopmode
+import android.animation.Animator
+import android.animation.AnimatorListenerAdapter
+import android.animation.AnimatorSet
+import android.animation.ObjectAnimator
 import android.annotation.ColorInt
 import android.content.Context
 import android.graphics.Bitmap
@@ -23,6 +27,10 @@
 import android.util.TypedValue
 import android.view.MotionEvent.ACTION_OUTSIDE
 import android.view.SurfaceView
+import android.view.View
+import android.view.View.ALPHA
+import android.view.View.SCALE_X
+import android.view.View.SCALE_Y
 import android.view.ViewGroup.MarginLayoutParams
 import android.widget.LinearLayout
 import android.window.TaskSnapshot
@@ -39,7 +47,7 @@
     lateinit var menuView: ManageWindowsView
 
     /** Creates the base menu view and fills it with icon views. */
-    fun show(snapshotList: List<Pair<Int, TaskSnapshot>>,
+    fun createMenu(snapshotList: List<Pair<Int, TaskSnapshot>>,
              onIconClickListener: ((Int) -> Unit),
              onOutsideClickListener: (() -> Unit)): ManageWindowsView {
         menuView = ManageWindowsView(context, menuBackgroundColor).apply {
@@ -51,11 +59,24 @@
         return menuView
     }
 
+    /** Play the animation for opening the menu. */
+    fun animateOpen() {
+        menuView.animateOpen()
+    }
+
+    /**
+     * Play the animation for closing the menu. On finish, will run the provided callback,
+     * which will be responsible for removing the view from the container used in [addToContainer].
+     */
+    fun animateClose() {
+        menuView.animateClose { removeFromContainer() }
+    }
+
     /** Adds the menu view to the container responsible for displaying it. */
     abstract fun addToContainer(menuView: ManageWindowsView)
 
-    /** Dispose of the menu, perform needed cleanup. */
-    abstract fun close()
+    /** Removes the menu view from the container used in the method above */
+    abstract fun removeFromContainer()
 
     companion object {
         const val MANAGE_WINDOWS_MINIMUM_INSTANCES = 2
@@ -65,6 +86,8 @@
         private val context: Context,
         menuBackgroundColor: Int
     ) {
+        private val animators = mutableListOf<Animator>()
+        private val iconViews = mutableListOf<SurfaceView>()
         val rootView: LinearLayout = LinearLayout(context)
         var menuHeight = 0
         var menuWidth = 0
@@ -147,6 +170,7 @@
                     menuWidth += (instanceIconWidth + iconMargin).toInt()
                 }
                 rowLayout?.addView(appSnapshotButton)
+                iconViews += appSnapshotButton
                 appSnapshotButton.requestLayout()
                 rowLayout?.post {
                     appSnapshotButton.holder.surface
@@ -190,6 +214,78 @@
             }
         }
 
+        /** Play the animation for opening the menu. */
+        fun animateOpen() {
+            animateView(rootView, MENU_BOUNDS_SHRUNK_SCALE, MENU_BOUNDS_FULL_SCALE,
+                MENU_START_ALPHA, MENU_FULL_ALPHA)
+            for (view in iconViews) {
+                animateView(view, MENU_BOUNDS_SHRUNK_SCALE, MENU_BOUNDS_FULL_SCALE,
+                    MENU_START_ALPHA, MENU_FULL_ALPHA)
+            }
+            createAnimatorSet().start()
+        }
+
+        /** Play the animation for closing the menu. */
+        fun animateClose(callback: () -> Unit) {
+            animateView(rootView, MENU_BOUNDS_FULL_SCALE, MENU_BOUNDS_SHRUNK_SCALE,
+                MENU_FULL_ALPHA, MENU_START_ALPHA)
+            for (view in iconViews) {
+                animateView(view, MENU_BOUNDS_FULL_SCALE, MENU_BOUNDS_SHRUNK_SCALE,
+                    MENU_FULL_ALPHA, MENU_START_ALPHA)
+            }
+            createAnimatorSet().apply {
+                addListener(
+                    object : AnimatorListenerAdapter() {
+                        override fun onAnimationEnd(animation: Animator) {
+                            callback.invoke()
+                        }
+                    }
+                )
+                start()
+            }
+        }
+
+        private fun animateView(
+            view: View,
+            startBoundsScale: Float,
+            endBoundsScale: Float,
+            startAlpha: Float,
+            endAlpha: Float) {
+            animators += ObjectAnimator.ofFloat(
+                view,
+                SCALE_X,
+                startBoundsScale,
+                endBoundsScale
+            ).apply {
+                duration = MENU_BOUNDS_ANIM_DURATION
+            }
+            animators += ObjectAnimator.ofFloat(
+                view,
+                SCALE_Y,
+                startBoundsScale,
+                endBoundsScale
+            ).apply {
+                duration = MENU_BOUNDS_ANIM_DURATION
+            }
+            animators += ObjectAnimator.ofFloat(
+                view,
+                ALPHA,
+                startAlpha,
+                endAlpha
+            ).apply {
+                duration = MENU_ALPHA_ANIM_DURATION
+                startDelay = MENU_ALPHA_ANIM_DELAY
+            }
+        }
+
+        private fun createAnimatorSet(): AnimatorSet {
+            val animatorSet = AnimatorSet().apply {
+                playTogether(animators)
+            }
+            animators.clear()
+            return animatorSet
+        }
+
         companion object {
             private const val MENU_RADIUS_DP = 26f
             private const val ICON_WIDTH_DP = 204f
@@ -198,6 +294,13 @@
             private const val ICON_MARGIN_DP = 16f
             private const val MENU_ELEVATION_DP = 1f
             private const val MENU_MAX_ICONS_PER_ROW = 3
+            private const val MENU_BOUNDS_ANIM_DURATION = 200L
+            private const val MENU_BOUNDS_SHRUNK_SCALE = 0.8f
+            private const val MENU_BOUNDS_FULL_SCALE = 1f
+            private const val MENU_ALPHA_ANIM_DURATION = 100L
+            private const val MENU_ALPHA_ANIM_DELAY = 50L
+            private const val MENU_START_ALPHA = 0f
+            private const val MENU_FULL_ALPHA = 1f
         }
     }
 }
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/pip/PipContentOverlay.java b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/pip/PipContentOverlay.java
index 6c83d88..eb7ef14 100644
--- a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/pip/PipContentOverlay.java
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/pip/PipContentOverlay.java
@@ -63,8 +63,19 @@
      * @param currentBounds {@link Rect} of the current animation bounds.
      * @param fraction progress of the animation ranged from 0f to 1f.
      */
-    public abstract void onAnimationUpdate(SurfaceControl.Transaction atomicTx,
-            Rect currentBounds, float fraction);
+    public void onAnimationUpdate(SurfaceControl.Transaction atomicTx,
+            Rect currentBounds, float fraction) {}
+
+    /**
+     * Animates the internal {@link #mLeash} by a given fraction for a config-at-end transition.
+     * @param atomicTx {@link SurfaceControl.Transaction} to operate, you should not explicitly
+     *                 call apply on this transaction, it should be applied on the caller side.
+     * @param scale scaling to apply onto the overlay.
+     * @param fraction progress of the animation ranged from 0f to 1f.
+     * @param endBounds the final bounds PiP is animating into.
+     */
+    public void onAnimationUpdate(SurfaceControl.Transaction atomicTx,
+            float scale, float fraction, Rect endBounds) {}
 
     /** A {@link PipContentOverlay} uses solid color. */
     public static final class PipColorOverlay extends PipContentOverlay {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
index 4607a8e..f59fed9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
@@ -583,9 +583,8 @@
             }
             final boolean windowModeChanged =
                     data.getTaskInfo().getWindowingMode() != taskInfo.getWindowingMode();
-            final boolean visibilityChanged = data.getTaskInfo().isVisible != taskInfo.isVisible;
-            if (windowModeChanged || (taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM
-                    && visibilityChanged)) {
+            if (windowModeChanged
+                    || hasFreeformConfigurationChanged(data.getTaskInfo(), taskInfo)) {
                 mRecentTasks.ifPresent(recentTasks ->
                         recentTasks.onTaskRunningInfoChanged(taskInfo));
             }
@@ -606,6 +605,17 @@
         }
     }
 
+    private boolean hasFreeformConfigurationChanged(RunningTaskInfo oldTaskInfo,
+            RunningTaskInfo newTaskInfo) {
+        if (newTaskInfo.getWindowingMode() != WINDOWING_MODE_FREEFORM) {
+            return false;
+        }
+        return oldTaskInfo.isVisible != newTaskInfo.isVisible
+                || !oldTaskInfo.positionInParent.equals(newTaskInfo.positionInParent)
+                || !Objects.equals(oldTaskInfo.configuration.windowConfiguration.getAppBounds(),
+                newTaskInfo.configuration.windowConfiguration.getAppBounds());
+    }
+
     @Override
     public void onBackPressedOnTaskRoot(RunningTaskInfo taskInfo) {
         synchronized (mLock) {
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 73bc426..ff3dc33 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
@@ -19,6 +19,7 @@
 import static android.app.ActivityTaskManager.INVALID_TASK_ID;
 import static android.view.RemoteAnimationTarget.MODE_CLOSING;
 import static android.view.RemoteAnimationTarget.MODE_OPENING;
+import static android.view.WindowManager.TRANSIT_CHANGE;
 import static android.view.WindowManager.TRANSIT_CLOSE_PREPARE_BACK_NAVIGATION;
 import static android.window.TransitionInfo.FLAG_BACK_GESTURE_ANIMATED;
 import static android.window.TransitionInfo.FLAG_IS_WALLPAPER;
@@ -95,6 +96,7 @@
 import com.android.wm.shell.transition.Transitions;
 
 import java.io.PrintWriter;
+import java.util.ArrayList;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.function.Predicate;
 
@@ -1261,6 +1263,15 @@
             return handleCloseTransition(info, st, ft, finishCallback);
         }
 
+        @Override
+        public void onTransitionConsumed(@NonNull IBinder transition, boolean aborted,
+                @Nullable SurfaceControl.Transaction finishTransaction) {
+            if (transition == mClosePrepareTransition && aborted) {
+                mClosePrepareTransition = null;
+                applyFinishOpenTransition();
+            }
+        }
+
         void createClosePrepareTransition() {
             if (mClosePrepareTransition != null) {
                 Log.e(TAG, "Re-create close prepare transition");
@@ -1277,42 +1288,42 @@
             }
             // Copy initial changes to final transition
             final TransitionInfo init = mOpenTransitionInfo;
-            // find prepare open target
+            // Find prepare open target
             boolean openShowWallpaper = false;
-            ComponentName openComponent = null;
+            final ArrayList<OpenChangeInfo> targets = new ArrayList<>();
             int tmpSize;
-            int openTaskId = INVALID_TASK_ID;
-            WindowContainerToken openToken = null;
             for (int j = init.getChanges().size() - 1; j >= 0; --j) {
                 final TransitionInfo.Change change = init.getChanges().get(j);
-                if (change.hasFlags(FLAG_BACK_GESTURE_ANIMATED)) {
-                    openComponent = findComponentName(change);
-                    openTaskId = findTaskId(change);
-                    openToken = findToken(change);
+                if (change.hasFlags(FLAG_BACK_GESTURE_ANIMATED)
+                        && TransitionUtil.isOpeningMode(change.getMode())) {
+                    final ComponentName openComponent = findComponentName(change);
+                    final int openTaskId = findTaskId(change);
+                    final WindowContainerToken openToken = findToken(change);
+                    if (openComponent == null && openTaskId == INVALID_TASK_ID
+                            && openToken == null) {
+                        continue;
+                    }
+                    targets.add(new OpenChangeInfo(openComponent, openTaskId, openToken));
                     if (change.hasFlags(FLAG_SHOW_WALLPAPER)) {
                         openShowWallpaper = true;
                     }
-                    break;
                 }
             }
-            if (openComponent == null && openTaskId == INVALID_TASK_ID && openToken == null) {
+            if (targets.isEmpty()) {
                 // This shouldn't happen, but if that happen, consume the initial transition anyway.
                 Log.e(TAG, "Unable to merge following transition, cannot find the gesture "
                         + "animated target from the open transition=" + mOpenTransitionInfo);
                 mOpenTransitionInfo = null;
                 return;
             }
-            // find first non-prepare open target
+            // Find first non-prepare open target
             boolean isOpen = false;
             tmpSize = info.getChanges().size();
             for (int j = 0; j < tmpSize; ++j) {
                 final TransitionInfo.Change change = info.getChanges().get(j);
-                final ComponentName firstNonOpen = findComponentName(change);
-                final int firstTaskId = findTaskId(change);
-                if ((firstNonOpen != null && firstNonOpen != openComponent)
-                        || (firstTaskId != INVALID_TASK_ID && firstTaskId != openTaskId)) {
-                    // this is original close target, potential be close, but cannot determine from
-                    // it
+                if (isOpenChangeMatched(targets, change)) {
+                    // This is original close target, potential be close, but cannot determine
+                    // from it.
                     if (change.hasFlags(FLAG_BACK_GESTURE_ANIMATED)) {
                         isOpen = !TransitionUtil.isClosingMode(change.getMode());
                     } else {
@@ -1321,33 +1332,44 @@
                     }
                 }
             }
-
             if (!isOpen) {
                 // Close transition, the transition info should be:
-                // init info(open A & wallpaper)
-                // current info(close B target)
+                // init info(open A & wallpaper) => init info(open A & change B & wallpaper)
+                // current info(close B target) => current info(change A & close B)
                 // remove init info(open/change A target & wallpaper)
                 boolean moveToTop = false;
+                boolean excludeOpenTarget = false;
+                boolean mergePredictive = false;
                 for (int j = info.getChanges().size() - 1; j >= 0; --j) {
                     final TransitionInfo.Change change = info.getChanges().get(j);
-                    if (isSameChangeTarget(openComponent, openTaskId, openToken, change)) {
+                    if (isOpenChangeMatched(targets, change)) {
+                        if (TransitionUtil.isClosingMode(change.getMode())) {
+                            excludeOpenTarget = true;
+                        }
                         moveToTop = change.hasFlags(FLAG_MOVED_TO_TOP);
                         info.getChanges().remove(j);
                     } else if ((openShowWallpaper && change.hasFlags(FLAG_IS_WALLPAPER))
                             || !change.hasFlags(FLAG_BACK_GESTURE_ANIMATED)) {
                         info.getChanges().remove(j);
+                    } else if (!mergePredictive && TransitionUtil.isClosingMode(change.getMode())) {
+                        mergePredictive = true;
                     }
                 }
                 // Ignore merge if there is no close target
-                if (!info.getChanges().isEmpty()) {
+                if (!info.getChanges().isEmpty() && mergePredictive) {
                     tmpSize = init.getChanges().size();
                     for (int i = 0; i < tmpSize; ++i) {
                         final TransitionInfo.Change change = init.getChanges().get(i);
                         if (change.hasFlags(FLAG_IS_WALLPAPER)) {
                             continue;
                         }
-                        if (moveToTop) {
-                            if (isSameChangeTarget(openComponent, openTaskId, openToken, change)) {
+                        if (isOpenChangeMatched(targets, change)) {
+                            if (excludeOpenTarget) {
+                                // App has triggered another change during predictive back
+                                // transition, filter out predictive back target.
+                                continue;
+                            }
+                            if (moveToTop) {
                                 change.setFlags(change.getFlags() | FLAG_MOVED_TO_TOP);
                             }
                         }
@@ -1376,7 +1398,7 @@
                 if (nonBackClose && nonBackOpen) {
                     for (int j = info.getChanges().size() - 1; j >= 0; --j) {
                         final TransitionInfo.Change change = info.getChanges().get(j);
-                        if (isSameChangeTarget(openComponent, openTaskId, openToken, change)) {
+                        if (isOpenChangeMatched(targets, change)) {
                             info.getChanges().remove(j);
                         } else if ((openShowWallpaper && change.hasFlags(FLAG_IS_WALLPAPER))) {
                             info.getChanges().remove(j);
@@ -1503,14 +1525,17 @@
                 return false;
             }
             SurfaceControl openingLeash = null;
+            SurfaceControl closingLeash = null;
             if (mApps != null) {
                 for (int i = mApps.length - 1; i >= 0; --i) {
                     if (mApps[i].mode == MODE_OPENING) {
                         openingLeash = mApps[i].leash;
+                    } else if (mApps[i].mode == MODE_CLOSING) {
+                        closingLeash = mApps[i].leash;
                     }
                 }
             }
-            if (openingLeash != null) {
+            if (openingLeash != null && closingLeash != null) {
                 int rootIdx = -1;
                 for (int i = info.getChanges().size() - 1; i >= 0; --i) {
                     final TransitionInfo.Change c = info.getChanges().get(i);
@@ -1520,6 +1545,9 @@
                         st.reparent(c.getLeash(), openingLeash);
                         st.setAlpha(c.getLeash(), 1.0f);
                         rootIdx = TransitionUtil.rootIndexFor(c, info);
+                    } else if (c.hasFlags(FLAG_BACK_GESTURE_ANIMATED)
+                            && c.getMode() == TRANSIT_CHANGE) {
+                        st.reparent(c.getLeash(), closingLeash);
                     }
                 }
                 // The root leash and the leash of opening target should actually in the same level,
@@ -1659,9 +1687,21 @@
         final ComponentName openChange = findComponentName(change);
         final int firstTaskId = findTaskId(change);
         final WindowContainerToken openToken = findToken(change);
-        return (openChange != null && openChange == topActivity)
+        return (openChange != null && openChange.equals(topActivity))
                 || (firstTaskId != INVALID_TASK_ID && firstTaskId == taskId)
-                || (openToken != null && token == openToken);
+                || (openToken != null && openToken.equals(token));
+    }
+
+    static boolean isOpenChangeMatched(@NonNull ArrayList<OpenChangeInfo> targets,
+            TransitionInfo.Change change) {
+        for (int i = targets.size() - 1; i >= 0; --i) {
+            final OpenChangeInfo next = targets.get(i);
+            if (isSameChangeTarget(next.mOpenComponent, next.mOpenTaskId, next.mOpenToken,
+                    change)) {
+                return true;
+            }
+        }
+        return false;
     }
 
     private static boolean canBeTransitionTarget(TransitionInfo.Change change) {
@@ -1721,4 +1761,16 @@
             }
         }
     }
+
+    static class OpenChangeInfo {
+        final ComponentName mOpenComponent;
+        final int mOpenTaskId;
+        final WindowContainerToken mOpenToken;
+        OpenChangeInfo(ComponentName openComponent, int openTaskId,
+                WindowContainerToken openToken) {
+            mOpenComponent = openComponent;
+            mOpenTaskId = openTaskId;
+            mOpenToken = openToken;
+        }
+    }
 }
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 37e8ead..d1246d3 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
@@ -833,7 +833,7 @@
             // window to show this in, but we use a separate code path.
             // TODO(b/273312602): consider foldables where we do need a stack view when folded
             if (mLayerView == null) {
-                mLayerView = new BubbleBarLayerView(mContext, this, mBubbleData);
+                mLayerView = new BubbleBarLayerView(mContext, this, mBubbleData, mLogger);
                 mLayerView.setUnBubbleConversationCallback(mSysuiProxy::onUnbubbleConversation);
             }
         } else {
@@ -1759,6 +1759,9 @@
     @MainThread
     public void removeAllBubbles(@Bubbles.DismissReason int reason) {
         mBubbleData.dismissAll(reason);
+        if (reason == Bubbles.DISMISS_USER_GESTURE) {
+            mLogger.log(BubbleLogger.Event.BUBBLE_BAR_DISMISSED_DRAG_BAR);
+        }
     }
 
     private void onEntryAdded(BubbleEntry entry) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleLogger.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleLogger.java
index 6d757d2..3663073 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleLogger.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleLogger.java
@@ -145,6 +145,9 @@
         @UiEvent(doc = "bubble promoted from overflow back to bubble bar")
         BUBBLE_BAR_OVERFLOW_REMOVE_BACK_TO_BAR(1949),
 
+        @UiEvent(doc = "while bubble bar is expanded, switch to another/existing bubble")
+        BUBBLE_BAR_BUBBLE_SWITCHED(1977)
+
         // endregion
         ;
 
@@ -165,8 +168,14 @@
     }
 
     /**
-     * @param b Bubble involved in this UI event
-     * @param e UI event
+     * Log an UIEvent
+     */
+    public void log(UiEventLogger.UiEventEnum e) {
+        mUiEventLogger.log(e);
+    }
+
+    /**
+     * Log an UIEvent with the given bubble info
      */
     public void log(Bubble b, UiEventLogger.UiEventEnum e) {
         mUiEventLogger.logWithInstanceId(e, b.getAppUid(), b.getPackageName(), b.getInstanceId());
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskView.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskView.kt
index 65f8e48..68fc0c9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskView.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskView.kt
@@ -16,14 +16,11 @@
 
 package com.android.wm.shell.bubbles
 
-import android.app.ActivityTaskManager
 import android.app.ActivityTaskManager.INVALID_TASK_ID
 import android.content.ComponentName
-import android.os.RemoteException
-import android.util.Log
 import androidx.annotation.VisibleForTesting
+import com.android.wm.shell.Flags
 import com.android.wm.shell.taskview.TaskView
-import com.android.wm.shell.transition.Transitions.ENABLE_SHELL_TRANSITIONS
 import java.util.concurrent.Executor
 
 /**
@@ -89,21 +86,8 @@
      * This should be called after all other cleanup animations have finished.
      */
     fun cleanup() {
-        if (taskId != INVALID_TASK_ID) {
-            // Ensure the task is removed from WM
-            if (ENABLE_SHELL_TRANSITIONS) {
-                taskView.removeTask()
-            } else {
-                try {
-                    ActivityTaskManager.getService().removeTask(taskId)
-                } catch (e: RemoteException) {
-                    Log.w(TAG, e.message ?: "")
-                }
-            }
+        if (Flags.enableTaskViewControllerCleanup() || taskId != INVALID_TASK_ID) {
+            taskView.removeTask()
         }
     }
-
-    private companion object {
-        const val TAG = "BubbleTaskView"
-    }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/AnimatableScaleMatrix.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/AnimatableScaleMatrix.java
index 2612b81..e577c3e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/AnimatableScaleMatrix.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/AnimatableScaleMatrix.java
@@ -141,4 +141,10 @@
         // PhysicsAnimator's animator caching).
         return obj == this;
     }
+
+    @Override
+    public int hashCode() {
+        // Make sure equals and hashCode work in a similar way. Rely on object identity for both.
+        return System.identityHashCode(this);
+    }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewDragController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewDragController.kt
index 07463bb..34259bf 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewDragController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewDragController.kt
@@ -19,6 +19,7 @@
 import android.annotation.SuppressLint
 import android.view.MotionEvent
 import android.view.View
+import androidx.annotation.VisibleForTesting
 import com.android.wm.shell.bubbles.BubblePositioner
 import com.android.wm.shell.shared.bubbles.DismissView
 import com.android.wm.shell.shared.bubbles.RelativeTouchListener
@@ -32,7 +33,7 @@
     private val animationHelper: BubbleBarAnimationHelper,
     private val bubblePositioner: BubblePositioner,
     private val pinController: BubbleExpandedViewPinController,
-    private val dragListener: DragListener
+    @get:VisibleForTesting val dragListener: DragListener,
 ) {
 
     var isStuckToDismiss: Boolean = false
@@ -107,7 +108,7 @@
             viewInitialX: Float,
             viewInitialY: Float,
             dx: Float,
-            dy: Float
+            dy: Float,
         ) {
             if (!isMoving) {
                 isMoving = true
@@ -127,7 +128,7 @@
             dx: Float,
             dy: Float,
             velX: Float,
-            velY: Float
+            velY: Float,
         ) {
             finishDrag()
         }
@@ -152,7 +153,7 @@
     private inner class MagnetListener : MagnetizedObject.MagnetListener {
         override fun onStuckToTarget(
             target: MagnetizedObject.MagneticTarget,
-            draggedObject: MagnetizedObject<*>
+            draggedObject: MagnetizedObject<*>,
         ) {
             isStuckToDismiss = true
             pinController.onStuckToDismissTarget()
@@ -163,7 +164,7 @@
             draggedObject: MagnetizedObject<*>,
             velX: Float,
             velY: Float,
-            wasFlungOut: Boolean
+            wasFlungOut: Boolean,
         ) {
             isStuckToDismiss = false
             animationHelper.animateUnstuckFromDismissView(target)
@@ -171,7 +172,7 @@
 
         override fun onReleasedInTarget(
             target: MagnetizedObject.MagneticTarget,
-            draggedObject: MagnetizedObject<*>
+            draggedObject: MagnetizedObject<*>,
         ) {
             dragListener.onReleased(inDismiss = true)
             pinController.onDragEnd()
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 1367b7e..402818c 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
@@ -34,10 +34,12 @@
 import android.widget.FrameLayout;
 
 import androidx.annotation.NonNull;
+import androidx.annotation.VisibleForTesting;
 
 import com.android.wm.shell.bubbles.Bubble;
 import com.android.wm.shell.bubbles.BubbleController;
 import com.android.wm.shell.bubbles.BubbleData;
+import com.android.wm.shell.bubbles.BubbleLogger;
 import com.android.wm.shell.bubbles.BubbleOverflow;
 import com.android.wm.shell.bubbles.BubblePositioner;
 import com.android.wm.shell.bubbles.BubbleViewProvider;
@@ -69,6 +71,7 @@
     private final BubbleController mBubbleController;
     private final BubbleData mBubbleData;
     private final BubblePositioner mPositioner;
+    private final BubbleLogger mBubbleLogger;
     private final BubbleBarAnimationHelper mAnimationHelper;
     private final BubbleEducationViewController mEducationViewController;
     private final View mScrimView;
@@ -93,11 +96,13 @@
     private TouchDelegate mHandleTouchDelegate;
     private final Rect mHandleTouchBounds = new Rect();
 
-    public BubbleBarLayerView(Context context, BubbleController controller, BubbleData bubbleData) {
+    public BubbleBarLayerView(Context context, BubbleController controller, BubbleData bubbleData,
+            BubbleLogger bubbleLogger) {
         super(context);
         mBubbleController = controller;
         mBubbleData = bubbleData;
         mPositioner = mBubbleController.getPositioner();
+        mBubbleLogger = bubbleLogger;
 
         mAnimationHelper = new BubbleBarAnimationHelper(context,
                 this, mPositioner);
@@ -233,6 +238,11 @@
             DragListener dragListener = inDismiss -> {
                 if (inDismiss && mExpandedBubble != null) {
                     mBubbleController.dismissBubble(mExpandedBubble.getKey(), DISMISS_USER_GESTURE);
+                    if (mExpandedBubble instanceof Bubble) {
+                        // Only a bubble can be dragged to dismiss
+                        mBubbleLogger.log((Bubble) mExpandedBubble,
+                                BubbleLogger.Event.BUBBLE_BAR_BUBBLE_DISMISSED_DRAG_EXP_VIEW);
+                    }
                 }
             };
             mDragController = new BubbleBarExpandedViewDragController(
@@ -413,4 +423,10 @@
         }
     }
 
+    @Nullable
+    @VisibleForTesting
+    public BubbleBarExpandedViewDragController getDragController() {
+        return mDragController;
+    }
+
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleEducationViewController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleEducationViewController.kt
index 9fd255d..7adec39 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleEducationViewController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleEducationViewController.kt
@@ -20,6 +20,7 @@
 import android.graphics.Point
 import android.graphics.Rect
 import android.util.Log
+import android.view.Gravity
 import android.view.LayoutInflater
 import android.view.View
 import android.view.ViewGroup
@@ -102,14 +103,17 @@
         hideEducation(animated = false)
         log { "showStackEducation at: $position" }
 
+        val rootBounds = Rect()
+        // Get root bounds on screen as position is in screen coordinates
+        root.getBoundsOnScreen(rootBounds)
         educationView =
             createEducationView(R.layout.bubble_bar_stack_education, root).apply {
                 setArrowDirection(BubblePopupDrawable.ArrowDirection.DOWN)
-                setArrowPosition(BubblePopupDrawable.ArrowPosition.End)
-                updateEducationPosition(view = this, position, root)
+                updateEducationPosition(view = this, position, rootBounds)
                 val arrowToEdgeOffset = popupDrawable?.config?.cornerRadius ?: 0f
                 doOnLayout {
-                    it.pivotX = it.width - arrowToEdgeOffset
+                    it.pivotX = if (position.x < rootBounds.centerX())
+                        arrowToEdgeOffset else it.width - arrowToEdgeOffset
                     it.pivotY = it.height.toFloat()
                 }
                 setOnClickListener { educationClickHandler() }
@@ -218,12 +222,9 @@
      *
      * @param view the user education view to layout
      * @param position the reference position in Screen coordinates
-     * @param root the root view to use for the layout
+     * @param rootBounds bounds of the parent the education view is placed in
      */
-    private fun updateEducationPosition(view: BubblePopupView, position: Point, root: ViewGroup) {
-        val rootBounds = Rect()
-        // Get root bounds on screen as position is in screen coordinates
-        root.getBoundsOnScreen(rootBounds)
+    private fun updateEducationPosition(view: BubblePopupView, position: Point, rootBounds: Rect) {
         // Get the offset to the arrow from the edge of the education view
         val arrowToEdgeOffset =
             view.popupDrawable?.config?.let { it.cornerRadius + it.arrowWidth / 2f }?.roundToInt()
@@ -231,7 +232,15 @@
         // Calculate education view margins
         val params = view.layoutParams as FrameLayout.LayoutParams
         params.bottomMargin = rootBounds.bottom - position.y
-        params.rightMargin = rootBounds.right - position.x - arrowToEdgeOffset
+        if (position.x < rootBounds.centerX()) {
+            params.leftMargin = position.x - arrowToEdgeOffset
+            params.gravity = Gravity.LEFT or Gravity.BOTTOM
+            view.setArrowPosition(BubblePopupDrawable.ArrowPosition.Start)
+        } else {
+            params.rightMargin = rootBounds.right - position.x - arrowToEdgeOffset
+            params.gravity = Gravity.RIGHT or Gravity.BOTTOM
+            view.setArrowPosition(BubblePopupDrawable.ArrowPosition.End)
+        }
         view.layoutParams = params
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerHandleView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerHandleView.java
index bdbd4cf..6c04e2a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerHandleView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerHandleView.java
@@ -103,7 +103,8 @@
         mHoveringHeight = mHeight > mWidth ? ((int) (mHeight * 1.5f)) : mHeight;
     }
 
-    void setIsLeftRightSplit(boolean isLeftRightSplit) {
+    /** sets whether it's a left/right or top/bottom split */
+    public void setIsLeftRightSplit(boolean isLeftRightSplit) {
         mIsLeftRightSplit = isLeftRightSplit;
         updateDimens();
     }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerRoundedCorner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerRoundedCorner.java
index 834c15d..d5aaf75 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerRoundedCorner.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerRoundedCorner.java
@@ -98,7 +98,11 @@
         return false;
     }
 
-    void setIsLeftRightSplit(boolean isLeftRightSplit) {
+    /**
+     * Set whether the rounded corner is for a left/right split.
+     * @param isLeftRightSplit whether it's a left/right split or top/bottom split.
+     */
+    public void setIsLeftRightSplit(boolean isLeftRightSplit) {
         mIsLeftRightSplit = isLeftRightSplit;
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerSnapAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerSnapAlgorithm.java
index 8592170..b29e49a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerSnapAlgorithm.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerSnapAlgorithm.java
@@ -118,8 +118,16 @@
         mDisplayHeight = displayHeight;
         mIsLeftRightSplit = isLeftRightSplit;
         mInsets.set(insets);
-        mSnapMode = isMinimizedMode ? SNAP_MODE_MINIMIZED :
-                res.getInteger(com.android.internal.R.integer.config_dockedStackDividerSnapMode);
+        if (Flags.enableFlexibleTwoAppSplit()) {
+            // In flexible split, we always use a fixed ratio (50%, 66%, or 90%) for splitting
+            mSnapMode = SNAP_FIXED_RATIO;
+        } else {
+            // Set SNAP_MODE_MINIMIZED, SNAP_MODE_16_9, or SNAP_FIXED_RATIO depending on config
+            mSnapMode = isMinimizedMode
+                    ? SNAP_MODE_MINIMIZED
+                    : res.getInteger(
+                            com.android.internal.R.integer.config_dockedStackDividerSnapMode);
+        }
         mFreeSnapMode = res.getBoolean(
                 com.android.internal.R.bool.config_dockedStackDividerFreeSnapMode);
         mFixedRatio = res.getFraction(
@@ -129,8 +137,7 @@
         mCalculateRatiosBasedOnAvailableSpace = res.getBoolean(
                 com.android.internal.R.bool.config_flexibleSplitRatios);
         // If this is a small screen or a foldable, use offscreen ratios
-        mAllowOffscreenRatios =
-                Flags.enableFlexibleTwoAppSplit() && SplitScreenUtils.allowOffscreenRatios(res);
+        mAllowOffscreenRatios = SplitScreenUtils.allowOffscreenRatios(res);
         mTaskHeightInMinimizedMode = isHomeResizable ? res.getDimensionPixelSize(
                 com.android.internal.R.dimen.task_height_of_minimized_mode) : 0;
         calculateTargets(isLeftRightSplit, dockSide);
@@ -299,9 +306,9 @@
     private void addNonDismissingTargets(boolean isLeftRightSplit, int topPosition,
             int bottomPosition, int dividerMax) {
         @PersistentSnapPosition int firstTarget =
-                mAllowOffscreenRatios ? SNAP_TO_2_10_90 : SNAP_TO_2_33_66;
+                areOffscreenRatiosSupported() ? SNAP_TO_2_10_90 : SNAP_TO_2_33_66;
         @PersistentSnapPosition int lastTarget =
-                mAllowOffscreenRatios ? SNAP_TO_2_90_10 : SNAP_TO_2_66_33;
+                areOffscreenRatiosSupported() ? SNAP_TO_2_90_10 : SNAP_TO_2_66_33;
         maybeAddTarget(topPosition, topPosition - getStartInset(), firstTarget);
         addMiddleTarget(isLeftRightSplit);
         maybeAddTarget(bottomPosition,
@@ -313,14 +320,21 @@
         int end = isLeftRightSplit
                 ? mDisplayWidth - mInsets.right
                 : mDisplayHeight - mInsets.bottom;
-        int size = (int) (mFixedRatio * (end - start)) - mDividerSize / 2;
 
-        if (mAllowOffscreenRatios) {
-            // TODO (b/349828130): This is a placeholder value, real measurements to come
-            size = (int) (0.3f * (end - start)) - mDividerSize / 2;
-        } else if (mCalculateRatiosBasedOnAvailableSpace) {
-            size = Math.max(size, mMinimalSizeResizableTask);
+        int size;
+        if (Flags.enableFlexibleTwoAppSplit()) {
+            float ratio = areOffscreenRatiosSupported()
+                    ? SplitLayout.OFFSCREEN_ASYMMETRIC_RATIO
+                    : SplitLayout.ONSCREEN_ONLY_ASYMMETRIC_RATIO;
+            size = (int) (ratio * (end - start)) - mDividerSize / 2;
+        } else {
+            size = (int) (mFixedRatio * (end - start)) - mDividerSize / 2;
+
+            if (mCalculateRatiosBasedOnAvailableSpace) {
+                size = Math.max(size, mMinimalSizeResizableTask);
+            }
         }
+
         int topPosition = start + size;
         int bottomPosition = end - size - mDividerSize;
         addNonDismissingTargets(isLeftRightSplit, topPosition, bottomPosition, dividerMax);
@@ -347,7 +361,7 @@
      * meets the minimal size requirement.
      */
     private void maybeAddTarget(int position, int smallerSize, @SnapPosition int snapPosition) {
-        if (smallerSize >= mMinimalSizeResizableTask || mAllowOffscreenRatios) {
+        if (smallerSize >= mMinimalSizeResizableTask || areOffscreenRatiosSupported()) {
             mTargets.add(new SnapTarget(position, snapPosition));
         }
     }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java
index e7848e2..cf858de 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java
@@ -480,6 +480,7 @@
                     mLastDraggingPosition,
                     position,
                     mSplitLayout.FLING_RESIZE_DURATION,
+                    Interpolators.FAST_OUT_SLOW_IN,
                     () -> mSplitLayout.setDividerPosition(position, true /* applyLayoutChange */));
             mMoving = false;
         }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
index f73065ea..b1e0e9e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
@@ -28,6 +28,7 @@
 import static com.android.internal.jank.InteractionJankMonitor.CUJ_SPLIT_SCREEN_RESIZE;
 import static com.android.wm.shell.shared.animation.Interpolators.DIM_INTERPOLATOR;
 import static com.android.wm.shell.shared.animation.Interpolators.EMPHASIZED;
+import static com.android.wm.shell.shared.animation.Interpolators.FAST_OUT_SLOW_IN;
 import static com.android.wm.shell.shared.animation.Interpolators.LINEAR;
 import static com.android.wm.shell.shared.animation.Interpolators.SLOWDOWN_INTERPOLATOR;
 import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_2_10_90;
@@ -77,7 +78,6 @@
 import com.android.wm.shell.common.pip.PipUtils;
 import com.android.wm.shell.common.split.DividerSnapAlgorithm.SnapTarget;
 import com.android.wm.shell.protolog.ShellProtoLogGroup;
-import com.android.wm.shell.shared.animation.Interpolators;
 import com.android.wm.shell.shared.annotations.ShellMainThread;
 import com.android.wm.shell.shared.split.SplitScreenConstants.PersistentSnapPosition;
 import com.android.wm.shell.shared.split.SplitScreenConstants.SnapPosition;
@@ -100,6 +100,12 @@
     public static final int FLING_RESIZE_DURATION = 250;
     private static final int FLING_ENTER_DURATION = 450;
     private static final int FLING_EXIT_DURATION = 450;
+    private static final int FLING_OFFSCREEN_DURATION = 500;
+
+    /** A split ratio used on larger screens, where we can fit both apps onscreen. */
+    public static final float ONSCREEN_ONLY_ASYMMETRIC_RATIO = 0.33f;
+    /** A split ratio used on smaller screens, where we place one app mostly offscreen. */
+    public static final float OFFSCREEN_ASYMMETRIC_RATIO = 0.1f;
 
     // Here are some (arbitrarily decided) layer definitions used during animations to make sure the
     // layers stay in order. Note: This does not affect any other layer numbering systems because
@@ -604,25 +610,35 @@
      * Sets new divider position and updates bounds correspondingly. Notifies listener if the new
      * target indicates dismissing split.
      */
-    public void snapToTarget(int currentPosition, SnapTarget snapTarget) {
+    public void snapToTarget(int currentPosition, SnapTarget snapTarget, int duration,
+            Interpolator interpolator) {
         switch (snapTarget.snapPosition) {
             case SNAP_TO_START_AND_DISMISS:
-                flingDividerPosition(currentPosition, snapTarget.position, FLING_RESIZE_DURATION,
+                flingDividerPosition(currentPosition, snapTarget.position, duration, interpolator,
                         () -> mSplitLayoutHandler.onSnappedToDismiss(false /* bottomOrRight */,
                                 EXIT_REASON_DRAG_DIVIDER));
                 break;
             case SNAP_TO_END_AND_DISMISS:
-                flingDividerPosition(currentPosition, snapTarget.position, FLING_RESIZE_DURATION,
+                flingDividerPosition(currentPosition, snapTarget.position, duration, interpolator,
                         () -> mSplitLayoutHandler.onSnappedToDismiss(true /* bottomOrRight */,
                                 EXIT_REASON_DRAG_DIVIDER));
                 break;
             default:
-                flingDividerPosition(currentPosition, snapTarget.position, FLING_RESIZE_DURATION,
+                flingDividerPosition(currentPosition, snapTarget.position, duration, interpolator,
                         () -> setDividerPosition(snapTarget.position, true /* applyLayoutChange */));
                 break;
         }
     }
 
+    /**
+     * Same as {@link #snapToTarget(int, SnapTarget)}, with default animation duration and
+     * interpolator.
+     */
+    public void snapToTarget(int currentPosition, SnapTarget snapTarget) {
+        snapToTarget(currentPosition, snapTarget, FLING_RESIZE_DURATION,
+                FAST_OUT_SLOW_IN);
+    }
+
     void onStartDragging() {
         mInteractionJankMonitor.begin(getDividerLeash(), mContext, mHandler,
                 CUJ_SPLIT_SCREEN_RESIZE);
@@ -674,14 +690,14 @@
     public void flingDividerToDismiss(boolean toEnd, int reason) {
         final int target = toEnd ? mDividerSnapAlgorithm.getDismissEndTarget().position
                 : mDividerSnapAlgorithm.getDismissStartTarget().position;
-        flingDividerPosition(getDividerPosition(), target, FLING_EXIT_DURATION,
+        flingDividerPosition(getDividerPosition(), target, FLING_EXIT_DURATION, FAST_OUT_SLOW_IN,
                 () -> mSplitLayoutHandler.onSnappedToDismiss(toEnd, reason));
     }
 
     /** Fling divider from current position to center position. */
     public void flingDividerToCenter(@Nullable Runnable finishCallback) {
         final int pos = mDividerSnapAlgorithm.getMiddleTarget().position;
-        flingDividerPosition(getDividerPosition(), pos, FLING_ENTER_DURATION,
+        flingDividerPosition(getDividerPosition(), pos, FLING_ENTER_DURATION, FAST_OUT_SLOW_IN,
                 () -> {
                     setDividerPosition(pos, true /* applyLayoutChange */);
                     if (finishCallback != null) {
@@ -699,14 +715,16 @@
     public void flingDividerToOtherSide(@PersistentSnapPosition int currentSnapPosition) {
         switch (currentSnapPosition) {
             case SNAP_TO_2_10_90 ->
-                    snapToTarget(mDividerPosition, mDividerSnapAlgorithm.getLastSplitTarget());
+                    snapToTarget(mDividerPosition, mDividerSnapAlgorithm.getLastSplitTarget(),
+                            FLING_OFFSCREEN_DURATION, EMPHASIZED);
             case SNAP_TO_2_90_10 ->
-                    snapToTarget(mDividerPosition, mDividerSnapAlgorithm.getFirstSplitTarget());
+                    snapToTarget(mDividerPosition, mDividerSnapAlgorithm.getFirstSplitTarget(),
+                            FLING_OFFSCREEN_DURATION, EMPHASIZED);
         }
     }
 
     @VisibleForTesting
-    void flingDividerPosition(int from, int to, int duration,
+    void flingDividerPosition(int from, int to, int duration, Interpolator interpolator,
             @Nullable Runnable flingFinishedCallback) {
         if (from == to) {
             if (flingFinishedCallback != null) {
@@ -724,7 +742,7 @@
         mDividerFlingAnimator = ValueAnimator
                 .ofInt(from, to)
                 .setDuration(duration);
-        mDividerFlingAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
+        mDividerFlingAnimator.setInterpolator(interpolator);
 
         // If the divider is being physically controlled by the user, we use a cool parallax effect
         // on the task windows. So if this "snap" animation is an extension of a user-controlled
@@ -1048,6 +1066,14 @@
         return (int) (minWidth / density);
     }
 
+    public int getDisplayWidth() {
+        return mRootBounds.width();
+    }
+
+    public int getDisplayHeight() {
+        return mRootBounds.height();
+    }
+
     /**
      * Shift configuration bounds to prevent client apps get configuration changed or relaunch. And
      * restore shifted configuration bounds if it's no longer shifted.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenUtils.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenUtils.java
index 65bf389..9113c0a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenUtils.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenUtils.java
@@ -142,13 +142,25 @@
     }
 
     /**
+     * Convenience function for {@link #isLargeScreen(Configuration)}.
+     */
+    public static boolean isLargeScreen(Resources res) {
+        return isLargeScreen(res.getConfiguration());
+    }
+
+    /**
+     * Returns whether the current device is a foldable
+     */
+    public static boolean isFoldable(Resources res) {
+        return res.getIntArray(com.android.internal.R.array.config_foldedDeviceStates).length != 0;
+    }
+
+    /**
      * Returns whether we should allow split ratios to go offscreen or not. If the device is a phone
      * or a foldable (either screen), we allow it.
      */
     public static boolean allowOffscreenRatios(Resources res) {
-        return !isLargeScreen(res.getConfiguration())
-                ||
-                res.getIntArray(com.android.internal.R.array.config_foldedDeviceStates).length != 0;
+        return Flags.enableFlexibleTwoAppSplit() && (!isLargeScreen(res) || isFoldable(res));
     }
 
     /**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/OWNERS b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/OWNERS
new file mode 100644
index 0000000..752d2fd
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/OWNERS
@@ -0,0 +1,2 @@
+# WM Shell sub-module dagger owners
+jorgegil@google.com
\ No newline at end of file
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 7f54786..f3f91e6e 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
@@ -18,6 +18,7 @@
 
 import static android.window.DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_ENTER_TRANSITIONS;
 import static android.window.DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_TASK_LIMIT;
+import static android.window.DesktopModeFlags.ENABLE_WINDOWING_TRANSITION_HANDLERS_OBSERVERS;
 
 import android.annotation.Nullable;
 import android.app.KeyguardManager;
@@ -87,6 +88,8 @@
 import com.android.wm.shell.desktopmode.education.AppHandleEducationFilter;
 import com.android.wm.shell.desktopmode.education.data.AppHandleEducationDatastoreRepository;
 import com.android.wm.shell.desktopmode.persistence.DesktopPersistentRepository;
+import com.android.wm.shell.desktopmode.persistence.DesktopRepositoryInitializer;
+import com.android.wm.shell.desktopmode.persistence.DesktopRepositoryInitializerImpl;
 import com.android.wm.shell.draganddrop.DragAndDropController;
 import com.android.wm.shell.draganddrop.GlobalDragListener;
 import com.android.wm.shell.freeform.FreeformComponents;
@@ -130,6 +133,7 @@
 import com.android.wm.shell.windowdecor.WindowDecorViewModel;
 import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalSystemViewContainer;
 import com.android.wm.shell.windowdecor.education.DesktopWindowingEducationTooltipController;
+import com.android.wm.shell.windowdecor.tiling.DesktopTilingDecorViewModel;
 
 import dagger.Binds;
 import dagger.Lazy;
@@ -152,12 +156,7 @@
  * <p>This module only defines Shell dependencies for handheld SystemUI implementation. Common
  * dependencies should go into {@link WMShellBaseModule}.
  */
-@Module(
-        includes = {
-                WMShellBaseModule.class,
-                PipModule.class,
-                ShellBackAnimationModule.class
-        })
+@Module(includes = {WMShellBaseModule.class, PipModule.class, ShellBackAnimationModule.class})
 public abstract class WMShellModule {
 
     //
@@ -172,8 +171,7 @@
 
     @WMSingleton
     @Provides
-    static BubblePositioner provideBubblePositioner(Context context,
-            WindowManager windowManager) {
+    static BubblePositioner provideBubblePositioner(Context context, WindowManager windowManager) {
         return new BubblePositioner(context, windowManager);
     }
 
@@ -185,20 +183,22 @@
 
     @WMSingleton
     @Provides
-    static BubbleData provideBubbleData(Context context,
+    static BubbleData provideBubbleData(
+            Context context,
             BubbleLogger logger,
             BubblePositioner positioner,
             BubbleEducationController educationController,
             @ShellMainThread ShellExecutor mainExecutor,
             @ShellBackgroundThread ShellExecutor bgExecutor) {
-        return new BubbleData(context, logger, positioner, educationController, mainExecutor,
-                bgExecutor);
+        return new BubbleData(
+                context, logger, positioner, educationController, mainExecutor, bgExecutor);
     }
 
     // Note: Handler needed for LauncherApps.register
     @WMSingleton
     @Provides
-    static BubbleController provideBubbleController(Context context,
+    static BubbleController provideBubbleController(
+            Context context,
             ShellInit shellInit,
             ShellCommandHandler shellCommandHandler,
             ShellController shellController,
@@ -223,14 +223,38 @@
             Transitions transitions,
             SyncTransactionQueue syncQueue,
             IWindowManager wmService) {
-        return new BubbleController(context, shellInit, shellCommandHandler, shellController, data,
-                null /* synchronizer */, floatingContentCoordinator,
-                new BubbleDataRepository(launcherApps, mainExecutor, bgExecutor,
+        return new BubbleController(
+                context,
+                shellInit,
+                shellCommandHandler,
+                shellController,
+                data,
+                null /* synchronizer */,
+                floatingContentCoordinator,
+                new BubbleDataRepository(
+                        launcherApps,
+                        mainExecutor,
+                        bgExecutor,
                         new BubblePersistentRepository(context)),
-                statusBarService, windowManager, windowManagerShellWrapper, userManager,
-                launcherApps, logger, taskStackListener, organizer, positioner, displayController,
-                oneHandedOptional, dragAndDropController, mainExecutor, mainHandler, bgExecutor,
-                taskViewTransitions, transitions, syncQueue, wmService,
+                statusBarService,
+                windowManager,
+                windowManagerShellWrapper,
+                userManager,
+                launcherApps,
+                logger,
+                taskStackListener,
+                organizer,
+                positioner,
+                displayController,
+                oneHandedOptional,
+                dragAndDropController,
+                mainExecutor,
+                mainHandler,
+                bgExecutor,
+                taskViewTransitions,
+                transitions,
+                syncQueue,
+                wmService,
                 ProdBubbleProperties.INSTANCE);
     }
 
@@ -317,9 +341,7 @@
     @WMSingleton
     @Provides
     static AppToWebGenericLinksParser provideGenericLinksParser(
-            Context context,
-            @ShellMainThread ShellExecutor mainExecutor
-    ) {
+            Context context, @ShellMainThread ShellExecutor mainExecutor) {
         return new AppToWebGenericLinksParser(context, mainExecutor);
     }
 
@@ -327,8 +349,7 @@
     static AssistContentRequester provideAssistContentRequester(
             Context context,
             @ShellMainThread ShellExecutor shellExecutor,
-            @ShellBackgroundThread ShellExecutor bgExecutor
-    ) {
+            @ShellBackgroundThread ShellExecutor bgExecutor) {
         return new AssistContentRequester(context, shellExecutor, bgExecutor);
     }
 
@@ -365,15 +386,20 @@
             Optional<DesktopRepository> desktopRepository,
             Optional<DesktopTasksController> desktopTasksController,
             LaunchAdjacentController launchAdjacentController,
-            WindowDecorViewModel windowDecorViewModel) {
+            WindowDecorViewModel windowDecorViewModel,
+            Optional<TaskChangeListener> taskChangeListener) {
         // TODO(b/238217847): Temporarily add this check here until we can remove the dynamic
         //                    override for this controller from the base module
-        ShellInit init = FreeformComponents.isFreeformEnabled(context)
-                ? shellInit
-                : null;
-        return new FreeformTaskListener(context, init, shellTaskOrganizer,
-                desktopRepository, desktopTasksController, launchAdjacentController,
-                windowDecorViewModel);
+        ShellInit init = FreeformComponents.isFreeformEnabled(context) ? shellInit : null;
+        return new FreeformTaskListener(
+                context,
+                init,
+                shellTaskOrganizer,
+                desktopRepository,
+                desktopTasksController,
+                launchAdjacentController,
+                windowDecorViewModel,
+                taskChangeListener);
     }
 
     @WMSingleton
@@ -384,10 +410,7 @@
             @ShellMainThread ShellExecutor mainExecutor,
             @ShellAnimationThread ShellExecutor animExecutor) {
         return new FreeformTaskTransitionHandler(
-                transitions,
-                displayController,
-                mainExecutor,
-                animExecutor);
+                transitions, displayController, mainExecutor, animExecutor);
     }
 
     @WMSingleton
@@ -401,8 +424,13 @@
             Optional<TaskChangeListener> taskChangeListener,
             FocusTransitionObserver focusTransitionObserver) {
         return new FreeformTaskTransitionObserver(
-                context, shellInit, transitions, desktopImmersiveController,
-                windowDecorViewModel, taskChangeListener, focusTransitionObserver);
+                context,
+                shellInit,
+                transitions,
+                desktopImmersiveController,
+                windowDecorViewModel,
+                taskChangeListener,
+                focusTransitionObserver);
     }
 
     @WMSingleton
@@ -418,8 +446,8 @@
         } else {
             transitionStarter = freeformTaskTransitionHandler;
         }
-        return new FreeformTaskTransitionStarterInitializer(shellInit, windowDecorViewModel,
-                transitionStarter);
+        return new FreeformTaskTransitionStarterInitializer(
+                shellInit, windowDecorViewModel, transitionStarter);
     }
 
     //
@@ -430,7 +458,8 @@
     @WMSingleton
     @Provides
     @DynamicOverride
-    static OneHandedController provideOneHandedController(Context context,
+    static OneHandedController provideOneHandedController(
+            Context context,
             ShellInit shellInit,
             ShellCommandHandler shellCommandHandler,
             ShellController shellController,
@@ -442,9 +471,19 @@
             InteractionJankMonitor jankMonitor,
             @ShellMainThread ShellExecutor mainExecutor,
             @ShellMainThread Handler mainHandler) {
-        return OneHandedController.create(context, shellInit, shellCommandHandler, shellController,
-                windowManager, displayController, displayLayout, taskStackListener, jankMonitor,
-                uiEventLogger, mainExecutor, mainHandler);
+        return OneHandedController.create(
+                context,
+                shellInit,
+                shellCommandHandler,
+                shellController,
+                windowManager,
+                displayController,
+                displayLayout,
+                taskStackListener,
+                jankMonitor,
+                uiEventLogger,
+                mainExecutor,
+                mainHandler);
     }
 
     //
@@ -476,12 +515,29 @@
             MultiInstanceHelper multiInstanceHelper,
             @ShellMainThread ShellExecutor mainExecutor,
             @ShellMainThread Handler mainHandler) {
-        return new SplitScreenController(context, shellInit, shellCommandHandler, shellController,
-                shellTaskOrganizer, syncQueue, rootTaskDisplayAreaOrganizer, displayController,
-                displayImeController, displayInsetsController, dragAndDropController, transitions,
-                transactionPool, iconProvider, recentTasks, launchAdjacentController,
-                windowDecorViewModel, desktopTasksController, null /* stageCoordinator */,
-                multiInstanceHelper, mainExecutor, mainHandler);
+        return new SplitScreenController(
+                context,
+                shellInit,
+                shellCommandHandler,
+                shellController,
+                shellTaskOrganizer,
+                syncQueue,
+                rootTaskDisplayAreaOrganizer,
+                displayController,
+                displayImeController,
+                displayInsetsController,
+                dragAndDropController,
+                transitions,
+                transactionPool,
+                iconProvider,
+                recentTasks,
+                launchAdjacentController,
+                windowDecorViewModel,
+                desktopTasksController,
+                null /* stageCoordinator */,
+                multiInstanceHelper,
+                mainExecutor,
+                mainHandler);
     }
 
     //
@@ -501,10 +557,16 @@
             Optional<UnfoldTransitionHandler> unfoldHandler,
             Optional<ActivityEmbeddingController> activityEmbeddingController,
             Transitions transitions) {
-        return new DefaultMixedHandler(shellInit, transitions, splitScreenOptional,
-                pipTransitionController, recentsTransitionHandler,
-                keyguardTransitionHandler, desktopTasksController,
-                unfoldHandler, activityEmbeddingController);
+        return new DefaultMixedHandler(
+                shellInit,
+                transitions,
+                splitScreenOptional,
+                pipTransitionController,
+                recentsTransitionHandler,
+                keyguardTransitionHandler,
+                desktopTasksController,
+                unfoldHandler,
+                activityEmbeddingController);
     }
 
     @WMSingleton
@@ -515,8 +577,12 @@
             Transitions transitions,
             Optional<RecentTasksController> recentTasksController,
             HomeTransitionObserver homeTransitionObserver) {
-        return new RecentsTransitionHandler(shellInit, shellTaskOrganizer, transitions,
-                recentTasksController.orElse(null), homeTransitionObserver);
+        return new RecentsTransitionHandler(
+                shellInit,
+                shellTaskOrganizer,
+                transitions,
+                recentTasksController.orElse(null),
+                homeTransitionObserver);
     }
 
     //
@@ -533,8 +599,7 @@
             FullscreenUnfoldTaskAnimator fullscreenAnimator,
             Lazy<Optional<UnfoldTransitionHandler>> unfoldTransitionHandler,
             ShellInit shellInit,
-            @ShellMainThread ShellExecutor mainExecutor
-    ) {
+            @ShellMainThread ShellExecutor mainExecutor) {
         final List<UnfoldTaskAnimator> animators = new ArrayList<>();
         animators.add(splitAnimator);
         animators.add(fullscreenAnimator);
@@ -545,8 +610,7 @@
                 progressProvider.get(),
                 animators,
                 unfoldTransitionHandler,
-                mainExecutor
-        );
+                mainExecutor);
     }
 
     @Provides
@@ -554,10 +618,9 @@
             Context context,
             UnfoldBackgroundController unfoldBackgroundController,
             ShellController shellController,
-            DisplayInsetsController displayInsetsController
-    ) {
-        return new FullscreenUnfoldTaskAnimator(context, unfoldBackgroundController,
-                shellController, displayInsetsController);
+            DisplayInsetsController displayInsetsController) {
+        return new FullscreenUnfoldTaskAnimator(
+                context, unfoldBackgroundController, shellController, displayInsetsController);
     }
 
     @Provides
@@ -567,14 +630,18 @@
             ShellController shellController,
             @ShellMainThread ShellExecutor executor,
             Lazy<Optional<SplitScreenController>> splitScreenOptional,
-            DisplayInsetsController displayInsetsController
-    ) {
+            DisplayInsetsController displayInsetsController) {
         // TODO(b/238217847): The lazy reference here causes some dependency issues since it
         // immediately registers a listener on that controller on init.  We should reference the
         // controller directly once we refactor ShellTaskOrganizer to not depend on the unfold
         // animation controller directly.
-        return new SplitTaskUnfoldAnimator(context, executor, splitScreenOptional,
-                shellController, backgroundController, displayInsetsController);
+        return new SplitTaskUnfoldAnimator(
+                context,
+                executor,
+                splitScreenOptional,
+                shellController,
+                backgroundController,
+                displayInsetsController);
     }
 
     @WMSingleton
@@ -601,8 +668,15 @@
             @ShellMainThread ShellExecutor executor,
             @ShellMainThread Handler handler,
             ShellInit shellInit) {
-        return new UnfoldTransitionHandler(shellInit, progressProvider.get(), animator,
-                unfoldAnimator, transactionPool, executor, handler, transitions);
+        return new UnfoldTransitionHandler(
+                shellInit,
+                progressProvider.get(),
+                animator,
+                unfoldAnimator,
+                transactionPool,
+                executor,
+                handler,
+                transitions);
     }
 
     @WMSingleton
@@ -631,6 +705,7 @@
             Transitions transitions,
             KeyguardManager keyguardManager,
             ReturnToDragStartAnimator returnToDragStartAnimator,
+            Optional<DesktopMixedTransitionHandler> desktopMixedTransitionHandler,
             EnterDesktopTaskTransitionHandler enterDesktopTransitionHandler,
             ExitDesktopTaskTransitionHandler exitDesktopTransitionHandler,
             DesktopModeDragAndDropTransitionHandler desktopModeDragAndDropTransitionHandler,
@@ -649,28 +724,75 @@
             InteractionJankMonitor interactionJankMonitor,
             InputManager inputManager,
             FocusTransitionObserver focusTransitionObserver,
-            DesktopModeEventLogger desktopModeEventLogger) {
-        return new DesktopTasksController(context, shellInit, shellCommandHandler, shellController,
-                displayController, shellTaskOrganizer, syncQueue, rootTaskDisplayAreaOrganizer,
-                dragAndDropController, transitions, keyguardManager,
-                returnToDragStartAnimator, enterDesktopTransitionHandler,
-                exitDesktopTransitionHandler, desktopModeDragAndDropTransitionHandler,
+            DesktopModeEventLogger desktopModeEventLogger,
+            DesktopTilingDecorViewModel desktopTilingDecorViewModel) {
+        return new DesktopTasksController(
+                context,
+                shellInit,
+                shellCommandHandler,
+                shellController,
+                displayController,
+                shellTaskOrganizer,
+                syncQueue,
+                rootTaskDisplayAreaOrganizer,
+                dragAndDropController,
+                transitions,
+                keyguardManager,
+                returnToDragStartAnimator,
+                desktopMixedTransitionHandler.get(),
+                enterDesktopTransitionHandler,
+                exitDesktopTransitionHandler,
+                desktopModeDragAndDropTransitionHandler,
                 toggleResizeDesktopTaskTransitionHandler,
-                dragToDesktopTransitionHandler, desktopImmersiveController.get(),
+                dragToDesktopTransitionHandler,
+                desktopImmersiveController.get(),
                 desktopRepository,
-                desktopModeLoggerTransitionObserver, launchAdjacentController,
-                recentsTransitionHandler, multiInstanceHelper, mainExecutor, desktopTasksLimiter,
-                recentTasksController.orElse(null), interactionJankMonitor, mainHandler,
-                inputManager, focusTransitionObserver,
-                desktopModeEventLogger);
+                desktopModeLoggerTransitionObserver,
+                launchAdjacentController,
+                recentsTransitionHandler,
+                multiInstanceHelper,
+                mainExecutor,
+                desktopTasksLimiter,
+                recentTasksController.orElse(null),
+                interactionJankMonitor,
+                mainHandler,
+                inputManager,
+                focusTransitionObserver,
+                desktopModeEventLogger,
+                desktopTilingDecorViewModel);
     }
 
     @WMSingleton
     @Provides
-    static Optional<TaskChangeListener> provideDesktopTaskChangeListener(Context context) {
-        if (Flags.enableWindowingTransitionHandlersObservers() &&
-                DesktopModeStatus.canEnterDesktopMode(context)) {
-            return Optional.of(new DesktopTaskChangeListener());
+    static DesktopTilingDecorViewModel provideDesktopTilingViewModel(Context context,
+            DisplayController displayController,
+            RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
+            SyncTransactionQueue syncQueue,
+            Transitions transitions,
+            ShellTaskOrganizer shellTaskOrganizer,
+            ToggleResizeDesktopTaskTransitionHandler toggleResizeDesktopTaskTransitionHandler,
+            ReturnToDragStartAnimator returnToDragStartAnimator,
+            @DynamicOverride DesktopRepository desktopRepository) {
+        return new DesktopTilingDecorViewModel(
+                context,
+                displayController,
+                rootTaskDisplayAreaOrganizer,
+                syncQueue,
+                transitions,
+                shellTaskOrganizer,
+                toggleResizeDesktopTaskTransitionHandler,
+                returnToDragStartAnimator,
+                desktopRepository
+        );
+    }
+
+    @WMSingleton
+    @Provides
+    static Optional<TaskChangeListener> provideDesktopTaskChangeListener(
+            Context context, @DynamicOverride DesktopRepository desktopRepository) {
+        if (ENABLE_WINDOWING_TRANSITION_HANDLERS_OBSERVERS.isTrue()
+                && DesktopModeStatus.canEnterDesktopMode(context)) {
+            return Optional.of(new DesktopTaskChangeListener(desktopRepository));
         }
         return Optional.empty();
     }
@@ -698,8 +820,7 @@
                         maxTaskLimit,
                         interactionJankMonitor,
                         context,
-                        handler)
-        );
+                        handler));
     }
 
     @WMSingleton
@@ -713,10 +834,7 @@
         if (DesktopModeStatus.canEnterDesktopMode(context)) {
             return Optional.of(
                     new DesktopImmersiveController(
-                            transitions,
-                            desktopRepository,
-                            displayController,
-                            shellTaskOrganizer));
+                            transitions, desktopRepository, displayController, shellTaskOrganizer));
         }
         return Optional.empty();
     }
@@ -728,7 +846,6 @@
         return new ReturnToDragStartAnimator(context, interactionJankMonitor);
     }
 
-
     @WMSingleton
     @Provides
     static DragToDesktopTransitionHandler provideDragToDesktopTransitionHandler(
@@ -736,12 +853,12 @@
             Transitions transitions,
             RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
             InteractionJankMonitor interactionJankMonitor) {
-        return (Flags.enableDesktopWindowingTransitions() ||
-            ENABLE_DESKTOP_WINDOWING_ENTER_TRANSITIONS.isTrue())
-                ? new SpringDragToDesktopTransitionHandler(context, transitions,
-                        rootTaskDisplayAreaOrganizer, interactionJankMonitor)
-                : new DefaultDragToDesktopTransitionHandler(context, transitions,
-                        rootTaskDisplayAreaOrganizer, interactionJankMonitor);
+        return (Flags.enableDesktopWindowingTransitions()
+                        || ENABLE_DESKTOP_WINDOWING_ENTER_TRANSITIONS.isTrue())
+                ? new SpringDragToDesktopTransitionHandler(
+                        context, transitions, rootTaskDisplayAreaOrganizer, interactionJankMonitor)
+                : new DefaultDragToDesktopTransitionHandler(
+                        context, transitions, rootTaskDisplayAreaOrganizer, interactionJankMonitor);
     }
 
     @WMSingleton
@@ -776,30 +893,29 @@
     static CloseDesktopTaskTransitionHandler provideCloseDesktopTaskTransitionHandler(
             Context context,
             @ShellMainThread ShellExecutor mainExecutor,
-            @ShellAnimationThread ShellExecutor animExecutor
-    ) {
+            @ShellAnimationThread ShellExecutor animExecutor) {
         return new CloseDesktopTaskTransitionHandler(context, mainExecutor, animExecutor);
     }
 
     @WMSingleton
     @Provides
     static DesktopModeDragAndDropTransitionHandler provideDesktopModeDragAndDropTransitionHandler(
-            Transitions transitions
-    ) {
+            Transitions transitions) {
         return new DesktopModeDragAndDropTransitionHandler(transitions);
     }
 
     @WMSingleton
     @Provides
     @DynamicOverride
-
     static DesktopRepository provideDesktopRepository(
             Context context,
             ShellInit shellInit,
             DesktopPersistentRepository desktopPersistentRepository,
+            DesktopRepositoryInitializer desktopRepositoryInitializer,
             @ShellMainThread CoroutineScope mainScope
     ) {
         return new DesktopRepository(context, shellInit, desktopPersistentRepository,
+                desktopRepositoryInitializer,
                 mainScope);
     }
 
@@ -811,12 +927,16 @@
             ShellTaskOrganizer shellTaskOrganizer,
             TaskStackListenerImpl taskStackListener,
             ToggleResizeDesktopTaskTransitionHandler toggleResizeDesktopTaskTransitionHandler,
-            @DynamicOverride DesktopRepository desktopRepository
-    ) {
+            @DynamicOverride DesktopRepository desktopRepository) {
         if (DesktopModeStatus.canEnterDesktopMode(context)) {
-            return Optional.of(new DesktopActivityOrientationChangeHandler(
-                    context, shellInit, shellTaskOrganizer, taskStackListener,
-                    toggleResizeDesktopTaskTransitionHandler, desktopRepository));
+            return Optional.of(
+                    new DesktopActivityOrientationChangeHandler(
+                            context,
+                            shellInit,
+                            shellTaskOrganizer,
+                            taskStackListener,
+                            toggleResizeDesktopTaskTransitionHandler,
+                            desktopRepository));
         }
         return Optional.empty();
     }
@@ -828,12 +948,16 @@
             Optional<DesktopRepository> desktopRepository,
             Transitions transitions,
             ShellTaskOrganizer shellTaskOrganizer,
-            ShellInit shellInit
-    ) {
-        return desktopRepository.flatMap(repository ->
-                Optional.of(new DesktopTasksTransitionObserver(
-                        context, repository, transitions, shellTaskOrganizer, shellInit))
-        );
+            ShellInit shellInit) {
+        return desktopRepository.flatMap(
+                repository ->
+                        Optional.of(
+                                new DesktopTasksTransitionObserver(
+                                        context,
+                                        repository,
+                                        transitions,
+                                        shellTaskOrganizer,
+                                        shellInit)));
     }
 
     @WMSingleton
@@ -844,9 +968,9 @@
             @DynamicOverride DesktopRepository desktopRepository,
             FreeformTaskTransitionHandler freeformTaskTransitionHandler,
             CloseDesktopTaskTransitionHandler closeDesktopTaskTransitionHandler,
+            Optional<DesktopImmersiveController> desktopImmersiveController,
             InteractionJankMonitor interactionJankMonitor,
-            @ShellMainThread Handler handler
-    ) {
+            @ShellMainThread Handler handler) {
         if (!DesktopModeStatus.canEnterDesktopMode(context)) {
             return Optional.empty();
         }
@@ -857,6 +981,7 @@
                         desktopRepository,
                         freeformTaskTransitionHandler,
                         closeDesktopTaskTransitionHandler,
+                        desktopImmersiveController.get(),
                         interactionJankMonitor,
                         handler));
     }
@@ -903,12 +1028,11 @@
     @Provides
     static DesktopWindowingEducationTooltipController
             provideDesktopWindowingEducationTooltipController(
-            Context context,
-            AdditionalSystemViewContainer.Factory additionalSystemViewContainerFactory,
-            DisplayController displayController
-    ) {
-        return new DesktopWindowingEducationTooltipController(context,
-                additionalSystemViewContainerFactory, displayController);
+                    Context context,
+                    AdditionalSystemViewContainer.Factory additionalSystemViewContainerFactory,
+                    DisplayController displayController) {
+        return new DesktopWindowingEducationTooltipController(
+                context, additionalSystemViewContainerFactory, displayController);
     }
 
     @OptIn(markerClass = ExperimentalCoroutinesApi.class)
@@ -920,22 +1044,35 @@
             AppHandleEducationDatastoreRepository appHandleEducationDatastoreRepository,
             WindowDecorCaptionHandleRepository windowDecorCaptionHandleRepository,
             DesktopWindowingEducationTooltipController desktopWindowingEducationTooltipController,
-            @ShellMainThread CoroutineScope applicationScope, @ShellBackgroundThread
-            MainCoroutineDispatcher backgroundDispatcher) {
-        return new AppHandleEducationController(context, appHandleEducationFilter,
-                appHandleEducationDatastoreRepository, windowDecorCaptionHandleRepository,
-                desktopWindowingEducationTooltipController, applicationScope,
+            @ShellMainThread CoroutineScope applicationScope,
+            @ShellBackgroundThread MainCoroutineDispatcher backgroundDispatcher) {
+        return new AppHandleEducationController(
+                context,
+                appHandleEducationFilter,
+                appHandleEducationDatastoreRepository,
+                windowDecorCaptionHandleRepository,
+                desktopWindowingEducationTooltipController,
+                applicationScope,
                 backgroundDispatcher);
     }
 
     @WMSingleton
     @Provides
     static DesktopPersistentRepository provideDesktopPersistentRepository(
-            Context context,
-            @ShellBackgroundThread CoroutineScope bgScope) {
+            Context context, @ShellBackgroundThread CoroutineScope bgScope) {
         return new DesktopPersistentRepository(context, bgScope);
     }
 
+    @WMSingleton
+    @Provides
+    static DesktopRepositoryInitializer provideDesktopRepositoryInitializer(
+            Context context,
+            DesktopPersistentRepository desktopPersistentRepository,
+            @ShellMainThread CoroutineScope mainScope) {
+        return new DesktopRepositoryInitializerImpl(context, desktopPersistentRepository,
+                mainScope);
+    }
+
     //
     // Drag and drop
     //
@@ -943,14 +1080,14 @@
     @WMSingleton
     @Provides
     static GlobalDragListener provideGlobalDragListener(
-            IWindowManager wmService,
-            @ShellMainThread ShellExecutor mainExecutor) {
+            IWindowManager wmService, @ShellMainThread ShellExecutor mainExecutor) {
         return new GlobalDragListener(wmService, mainExecutor);
     }
 
     @WMSingleton
     @Provides
-    static DragAndDropController provideDragAndDropController(Context context,
+    static DragAndDropController provideDragAndDropController(
+            Context context,
             ShellInit shellInit,
             ShellController shellController,
             ShellCommandHandler shellCommandHandler,
@@ -961,9 +1098,18 @@
             GlobalDragListener globalDragListener,
             Transitions transitions,
             @ShellMainThread ShellExecutor mainExecutor) {
-        return new DragAndDropController(context, shellInit, shellController, shellCommandHandler,
-                shellTaskOrganizer, displayController, uiEventLogger, iconProvider,
-                globalDragListener, transitions, mainExecutor);
+        return new DragAndDropController(
+                context,
+                shellInit,
+                shellController,
+                shellCommandHandler,
+                shellTaskOrganizer,
+                displayController,
+                uiEventLogger,
+                iconProvider,
+                globalDragListener,
+                transitions,
+                mainExecutor);
     }
 
     //
@@ -977,8 +1123,7 @@
     @Provides
     static Object provideIndependentShellComponentsToCreate(
             DragAndDropController dragAndDropController,
-            Optional<DesktopTasksTransitionObserver> desktopTasksTransitionObserverOptional
-    ) {
+            Optional<DesktopTasksTransitionObserver> desktopTasksTransitionObserverOptional) {
         return new Object();
     }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopImmersiveController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopImmersiveController.kt
index d0bc5f0..e741892 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopImmersiveController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopImmersiveController.kt
@@ -130,7 +130,8 @@
         displayId: Int
     ) {
         if (!Flags.enableFullyImmersiveInDesktop()) return
-        exitImmersiveIfApplicable(wct, displayId)?.invoke(transition)
+        val result = exitImmersiveIfApplicable(wct, displayId)
+        result.asExit()?.runOnTransitionStart?.invoke(transition)
     }
 
     /**
@@ -145,16 +146,23 @@
         wct: WindowContainerTransaction,
         displayId: Int,
         excludeTaskId: Int? = null,
-    ): ((IBinder) -> Unit)? {
-        if (!Flags.enableFullyImmersiveInDesktop()) return null
-        val immersiveTask = desktopRepository.getTaskInFullImmersiveState(displayId) ?: return null
+    ): ExitResult {
+        if (!Flags.enableFullyImmersiveInDesktop()) return ExitResult.NoExit
+        val immersiveTask = desktopRepository.getTaskInFullImmersiveState(displayId)
+            ?: return ExitResult.NoExit
         if (immersiveTask == excludeTaskId) {
-            return null
+            return ExitResult.NoExit
         }
-        val taskInfo = shellTaskOrganizer.getRunningTaskInfo(immersiveTask) ?: return null
+        val taskInfo = shellTaskOrganizer.getRunningTaskInfo(immersiveTask)
+            ?: return ExitResult.NoExit
         logV("Appending immersive exit for task: $immersiveTask in display: $displayId")
         wct.setBounds(taskInfo.token, getExitDestinationBounds(taskInfo))
-        return { transition -> addPendingImmersiveExit(immersiveTask, displayId, transition) }
+        return ExitResult.Exit(
+            exitingTask = immersiveTask,
+            runOnTransitionStart = { transition ->
+                addPendingImmersiveExit(immersiveTask, displayId, transition)
+            }
+        )
     }
 
     /**
@@ -167,22 +175,25 @@
     fun exitImmersiveIfApplicable(
         wct: WindowContainerTransaction,
         taskInfo: RunningTaskInfo
-    ): ((IBinder) -> Unit)? {
-        if (!Flags.enableFullyImmersiveInDesktop()) return null
+    ): ExitResult {
+        if (!Flags.enableFullyImmersiveInDesktop()) return ExitResult.NoExit
         if (desktopRepository.isTaskInFullImmersiveState(taskInfo.taskId)) {
             // A full immersive task is being minimized, make sure the immersive state is broken
             // (i.e. resize back to max bounds).
             wct.setBounds(taskInfo.token, getExitDestinationBounds(taskInfo))
             logV("Appending immersive exit for task: ${taskInfo.taskId}")
-            return { transition ->
-                addPendingImmersiveExit(
-                    taskId = taskInfo.taskId,
-                    displayId = taskInfo.displayId,
-                    transition = transition
-                )
-            }
+            return ExitResult.Exit(
+                exitingTask = taskInfo.taskId,
+                runOnTransitionStart = { transition ->
+                    addPendingImmersiveExit(
+                        taskId = taskInfo.taskId,
+                        displayId = taskInfo.displayId,
+                        transition = transition
+                    )
+                }
+            )
         }
-        return null
+        return ExitResult.NoExit
     }
 
 
@@ -461,6 +472,20 @@
         var transition: IBinder,
     )
 
+    /** The result of an external exit request. */
+    sealed class ExitResult {
+        /** An immersive task exit (meaning, resize) was appended to the request. */
+        data class Exit(
+            val exitingTask: Int,
+            val runOnTransitionStart: ((IBinder) -> Unit)
+        ) : ExitResult()
+        /** There was no exit appended to the request. */
+        data object NoExit : ExitResult()
+
+        /** Returns the result as an [Exit] or null if it isn't of that type. */
+        fun asExit(): Exit? = if (this is Exit) this else null
+    }
+
     private enum class Direction {
         ENTER, EXIT
     }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMixedTransitionHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMixedTransitionHandler.kt
index 54a07f2..0bc571f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMixedTransitionHandler.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMixedTransitionHandler.kt
@@ -27,15 +27,18 @@
 import android.window.TransitionInfo
 import android.window.TransitionRequestInfo
 import android.window.WindowContainerTransaction
+import androidx.annotation.VisibleForTesting
 import com.android.internal.jank.Cuj.CUJ_DESKTOP_MODE_EXIT_MODE_ON_LAST_WINDOW_CLOSE
 import com.android.internal.jank.InteractionJankMonitor
 import com.android.internal.protolog.ProtoLog
+import com.android.window.flags.Flags
 import com.android.wm.shell.freeform.FreeformTaskTransitionHandler
 import com.android.wm.shell.freeform.FreeformTaskTransitionStarter
 import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE
 import com.android.wm.shell.shared.annotations.ShellMainThread
 import com.android.wm.shell.transition.MixedTransitionHandler
 import com.android.wm.shell.transition.Transitions
+import com.android.wm.shell.transition.Transitions.TransitionFinishCallback
 
 /** The [Transitions.TransitionHandler] coordinates transition handlers in desktop windowing. */
 class DesktopMixedTransitionHandler(
@@ -44,10 +47,14 @@
     private val desktopRepository: DesktopRepository,
     private val freeformTaskTransitionHandler: FreeformTaskTransitionHandler,
     private val closeDesktopTaskTransitionHandler: CloseDesktopTaskTransitionHandler,
+    private val desktopImmersiveController: DesktopImmersiveController,
     private val interactionJankMonitor: InteractionJankMonitor,
     @ShellMainThread private val handler: Handler,
 ) : MixedTransitionHandler, FreeformTaskTransitionStarter {
 
+    @VisibleForTesting
+    val pendingMixedTransitions = mutableListOf<PendingMixedTransition>()
+
     /** Delegates starting transition to [FreeformTaskTransitionHandler]. */
     override fun startWindowingModeTransition(
         targetWindowingMode: Int,
@@ -65,6 +72,40 @@
         }
         requireNotNull(wct)
         return transitions.startTransition(WindowManager.TRANSIT_CLOSE, wct, /* handler= */ this)
+            .also { transition ->
+                pendingMixedTransitions.add(PendingMixedTransition.Close(transition))
+            }
+    }
+
+    /**
+     * Starts a launch transition for [taskId], with an optional [exitingImmersiveTask] if it was
+     * included in the [wct] and is expected to be animated by this handler.
+     */
+    fun startLaunchTransition(
+        @WindowManager.TransitionType transitionType: Int,
+        wct: WindowContainerTransaction,
+        taskId: Int,
+        exitingImmersiveTask: Int? = null,
+    ): IBinder {
+        if (!Flags.enableFullyImmersiveInDesktop()) {
+            return transitions.startTransition(transitionType, wct, /* handler= */ null)
+        }
+        if (exitingImmersiveTask == null) {
+            logV("Starting mixed launch transition for task#%d", taskId)
+        } else {
+            logV(
+                "Starting mixed launch transition for task#%d with immersive exit of task#%d",
+                taskId, exitingImmersiveTask
+            )
+        }
+        return transitions.startTransition(transitionType, wct, /* handler= */ this)
+            .also { transition ->
+                pendingMixedTransitions.add(PendingMixedTransition.Launch(
+                    transition = transition,
+                    launchingTask = taskId,
+                    exitingImmersiveTask = exitingImmersiveTask
+                ))
+            }
     }
 
     /** Returns null, as it only handles transitions started from Shell. */
@@ -78,11 +119,43 @@
         info: TransitionInfo,
         startTransaction: SurfaceControl.Transaction,
         finishTransaction: SurfaceControl.Transaction,
-        finishCallback: Transitions.TransitionFinishCallback,
+        finishCallback: TransitionFinishCallback,
+    ): Boolean {
+        val pending = pendingMixedTransitions.find { pending -> pending.transition == transition }
+            ?: return false.also {
+                logW("Should have pending desktop transition")
+            }
+        pendingMixedTransitions.remove(pending)
+        logV("Animating pending mixed transition: %s", pending)
+        return when (pending) {
+            is PendingMixedTransition.Close -> animateCloseTransition(
+                transition,
+                info,
+                startTransaction,
+                finishTransaction,
+                finishCallback
+            )
+            is PendingMixedTransition.Launch -> animateLaunchTransition(
+                pending,
+                transition,
+                info,
+                startTransaction,
+                finishTransaction,
+                finishCallback
+            )
+        }
+    }
+
+    private fun animateCloseTransition(
+        transition: IBinder,
+        info: TransitionInfo,
+        startTransaction: SurfaceControl.Transaction,
+        finishTransaction: SurfaceControl.Transaction,
+        finishCallback: TransitionFinishCallback,
     ): Boolean {
         val closeChange = findCloseDesktopTaskChange(info)
         if (closeChange == null) {
-            ProtoLog.w(WM_SHELL_DESKTOP_MODE, "%s: Should have closing desktop task", TAG)
+            logW("Should have closing desktop task")
             return false
         }
         if (isLastDesktopTask(closeChange)) {
@@ -106,6 +179,74 @@
         )
     }
 
+    private fun animateLaunchTransition(
+        pending: PendingMixedTransition.Launch,
+        transition: IBinder,
+        info: TransitionInfo,
+        startTransaction: SurfaceControl.Transaction,
+        finishTransaction: SurfaceControl.Transaction,
+        finishCallback: TransitionFinishCallback,
+    ): Boolean {
+        // Check if there's also an immersive change during this launch.
+        val immersiveExitChange = pending.exitingImmersiveTask?.let { exitingTask ->
+            findDesktopTaskChange(info, exitingTask)
+        }
+        val launchChange = findDesktopTaskChange(info, pending.launchingTask)
+            ?: error("Should have pending launching task change")
+
+        var subAnimationCount = -1
+        var combinedWct: WindowContainerTransaction? = null
+        val finishCb = TransitionFinishCallback { wct ->
+            --subAnimationCount
+            combinedWct = combinedWct.merge(wct)
+            if (subAnimationCount > 0) return@TransitionFinishCallback
+            finishCallback.onTransitionFinished(combinedWct)
+        }
+
+        logV(
+            "Animating pending mixed launch transition task#%d immersiveExitTask#%s",
+            launchChange.taskInfo!!.taskId, immersiveExitChange?.taskInfo?.taskId
+        )
+        if (immersiveExitChange != null) {
+            subAnimationCount = 2
+            // Animate the immersive exit change separately.
+            info.changes.remove(immersiveExitChange)
+            desktopImmersiveController.animateResizeChange(
+                immersiveExitChange,
+                startTransaction,
+                finishTransaction,
+                finishCb
+            )
+            // Let the leftover/default handler animate the remaining changes.
+            return dispatchToLeftoverHandler(
+                transition,
+                info,
+                startTransaction,
+                finishTransaction,
+                finishCb
+            )
+        }
+        // There's nothing to animate separately, so let the left over handler animate
+        // the entire transition.
+        subAnimationCount = 1
+        return dispatchToLeftoverHandler(
+            transition,
+            info,
+            startTransaction,
+            finishTransaction,
+            finishCb
+        )
+    }
+
+    override fun onTransitionConsumed(
+        transition: IBinder,
+        aborted: Boolean,
+        finishTransaction: SurfaceControl.Transaction?
+    ) {
+        pendingMixedTransitions.removeAll { pending -> pending.transition == transition }
+        super.onTransitionConsumed(transition, aborted, finishTransaction)
+    }
+
     /**
      * Dispatch close desktop task animation to the default transition handlers. Allows delegating
      * it to Launcher to animate in sync with show Home transition.
@@ -126,14 +267,34 @@
             CUJ_DESKTOP_MODE_EXIT_MODE_ON_LAST_WINDOW_CLOSE,
         )
         // Dispatch the last desktop task closing animation.
+        return dispatchToLeftoverHandler(
+            transition = transition,
+            info = info,
+            startTransaction = startTransaction,
+            finishTransaction = finishTransaction,
+            finishCallback = finishCallback,
+            doOnFinishCallback = {
+                // Finish the jank trace when closing the last window in desktop mode.
+                interactionJankMonitor.end(CUJ_DESKTOP_MODE_EXIT_MODE_ON_LAST_WINDOW_CLOSE)
+            }
+        )
+    }
+
+    private fun dispatchToLeftoverHandler(
+        transition: IBinder,
+        info: TransitionInfo,
+        startTransaction: SurfaceControl.Transaction,
+        finishTransaction: SurfaceControl.Transaction,
+        finishCallback: TransitionFinishCallback,
+        doOnFinishCallback: (() -> Unit)? = null,
+    ): Boolean {
         return transitions.dispatchTransition(
             transition,
             info,
             startTransaction,
             finishTransaction,
             { wct ->
-                // Finish the jank trace when closing the last window in desktop mode.
-                interactionJankMonitor.end(CUJ_DESKTOP_MODE_EXIT_MODE_ON_LAST_WINDOW_CLOSE)
+                doOnFinishCallback?.invoke()
                 finishCallback.onTransitionFinished(wct)
             },
             /* skip= */ this
@@ -155,6 +316,43 @@
         }
     }
 
+    private fun findDesktopTaskChange(info: TransitionInfo, taskId: Int): TransitionInfo.Change? {
+        return info.changes.firstOrNull { change -> change.taskInfo?.taskId == taskId }
+    }
+
+    private fun WindowContainerTransaction?.merge(
+        wct: WindowContainerTransaction?
+    ): WindowContainerTransaction? {
+        if (wct == null) return this
+        if (this == null) return wct
+        return this.merge(wct)
+    }
+
+    /** A scheduled transition that will potentially be animated by more than one handler */
+    sealed class PendingMixedTransition {
+        abstract val transition: IBinder
+
+        /** A task is closing. */
+        data class Close(
+            override val transition: IBinder,
+        ) : PendingMixedTransition()
+
+        /** A task is opening or moving to front. */
+        data class Launch(
+            override val transition: IBinder,
+            val launchingTask: Int,
+            val exitingImmersiveTask: Int?,
+        ) : PendingMixedTransition()
+    }
+
+    private fun logV(msg: String, vararg arguments: Any?) {
+        ProtoLog.v(WM_SHELL_DESKTOP_MODE, "%s: $msg", TAG, *arguments)
+    }
+
+    private fun logW(msg: String, vararg arguments: Any?) {
+        ProtoLog.w(WM_SHELL_DESKTOP_MODE, "%s: $msg", TAG, *arguments)
+    }
+
     companion object {
         private const val TAG = "DesktopMixedTransitionHandler"
     }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeEventLogger.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeEventLogger.kt
index 255ca6e..bed484c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeEventLogger.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeEventLogger.kt
@@ -541,7 +541,8 @@
                 FrameworkStatsLog.DESKTOP_MODE_UICHANGED__EXIT_REASON__RETURN_HOME_OR_OVERVIEW
             ),
             TASK_FINISHED(FrameworkStatsLog.DESKTOP_MODE_UICHANGED__EXIT_REASON__TASK_FINISHED),
-            SCREEN_OFF(FrameworkStatsLog.DESKTOP_MODE_UICHANGED__EXIT_REASON__SCREEN_OFF)
+            SCREEN_OFF(FrameworkStatsLog.DESKTOP_MODE_UICHANGED__EXIT_REASON__SCREEN_OFF),
+            TASK_MINIMIZED(FrameworkStatsLog.DESKTOP_MODE_UICHANGED__EXIT_REASON__TASK_MINIMIZED),
         }
 
         /**
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 ed03982..41febdf 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
@@ -382,6 +382,7 @@
             transitionInfo.type == TRANSIT_EXIT_DESKTOP_MODE_KEYBOARD_SHORTCUT ->
                 ExitReason.KEYBOARD_SHORTCUT_EXIT
             transitionInfo.isExitToRecentsTransition() -> ExitReason.RETURN_HOME_OR_OVERVIEW
+            transitionInfo.type == Transitions.TRANSIT_MINIMIZE -> ExitReason.TASK_MINIMIZED
             else -> {
                 ProtoLog.w(
                     WM_SHELL_DESKTOP_MODE,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt
index 5998dc8..fdb01ab 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt
@@ -30,7 +30,7 @@
 import com.android.internal.protolog.ProtoLog
 import com.android.window.flags.Flags
 import com.android.wm.shell.desktopmode.persistence.DesktopPersistentRepository
-import com.android.wm.shell.desktopmode.persistence.DesktopTaskState
+import com.android.wm.shell.desktopmode.persistence.DesktopRepositoryInitializer
 import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE
 import com.android.wm.shell.shared.annotations.ShellMainThread
 import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
@@ -46,6 +46,7 @@
     private val context: Context,
     shellInit: ShellInit,
     private val persistentRepository: DesktopPersistentRepository,
+    private val repositoryInitializer: DesktopRepositoryInitializer,
     @ShellMainThread private val mainCoroutineScope: CoroutineScope,
 ){
 
@@ -120,30 +121,7 @@
     }
 
     private fun initRepoFromPersistentStorage() {
-        if (!Flags.enableDesktopWindowingPersistence()) return
-        //  TODO: b/365962554 - Handle the case that user moves to desktop before it's initialized
-        mainCoroutineScope.launch {
-            val desktop = persistentRepository.readDesktop() ?: return@launch
-
-            val maxTasks =
-                DesktopModeStatus.getMaxTaskLimit(context).takeIf { it > 0 }
-                    ?: desktop.zOrderedTasksCount
-
-            desktop.zOrderedTasksList
-                // Reverse it so we initialize the repo from bottom to top.
-                .reversed()
-                .mapNotNull { taskId ->
-                    desktop.tasksByTaskIdMap[taskId]?.takeIf {
-                        it.desktopTaskState == DesktopTaskState.VISIBLE
-                    }
-                }
-                .take(maxTasks)
-                .forEach { task ->
-                    addOrMoveFreeformTaskToTop(desktop.displayId, task.taskId)
-                    addActiveTask(desktop.displayId, task.taskId)
-                    updateTaskVisibility(desktop.displayId, task.taskId, visible = false)
-                }
-        }
+        repositoryInitializer.initialize(this)
     }
 
     /** Adds [activeTasksListener] to be notified of updates to active tasks. */
@@ -199,8 +177,15 @@
         visibleTasksListeners.remove(visibleTasksListener)
     }
 
+    /** Adds task with [taskId] to the list of freeform tasks on [displayId]. */
+    fun addTask(displayId: Int, taskId: Int, isVisible: Boolean) {
+        addOrMoveFreeformTaskToTop(displayId, taskId)
+        addActiveTask(displayId, taskId)
+        updateTask(displayId, taskId, isVisible)
+    }
+
     /** Adds task with [taskId] to the list of active tasks on [displayId]. */
-    fun addActiveTask(displayId: Int, taskId: Int) {
+    private fun addActiveTask(displayId: Int, taskId: Int) {
         // Removes task if it is active on another display excluding [displayId].
         removeActiveTask(taskId, excludedDisplayId = displayId)
 
@@ -214,7 +199,7 @@
     fun removeActiveTask(taskId: Int, excludedDisplayId: Int? = null) {
         desktopTaskDataByDisplayId.forEach { displayId, desktopTaskData ->
             if ((displayId != excludedDisplayId)
-                    && desktopTaskData.activeTasks.remove(taskId)) {
+                && desktopTaskData.activeTasks.remove(taskId)) {
                 logD("Removed active task=%d displayId=%d", taskId, displayId)
                 updateActiveTasksListeners(displayId)
             }
@@ -287,8 +272,8 @@
      * If task was visible on a different display with a different [displayId], removes from
      * the set of visible tasks on that display and notifies listeners.
      */
-    fun updateTaskVisibility(displayId: Int, taskId: Int, visible: Boolean) {
-        if (visible) {
+    fun updateTask(displayId: Int, taskId: Int, isVisible: Boolean) {
+        if (isVisible) {
             // If task is visible, remove it from any other display besides [displayId].
             removeVisibleTask(taskId, excludedDisplayId = displayId)
         } else if (displayId == INVALID_DISPLAY) {
@@ -297,7 +282,7 @@
             return
         }
         val prevCount = getVisibleTaskCount(displayId)
-        if (visible) {
+        if (isVisible) {
             desktopTaskDataByDisplayId.getOrCreate(displayId).visibleTasks.add(taskId)
             unminimizeTask(displayId, taskId)
         } else {
@@ -306,7 +291,7 @@
         val newCount = getVisibleTaskCount(displayId)
         if (prevCount != newCount) {
             logD("Update task visibility taskId=%d visible=%b displayId=%d",
-                taskId, visible, displayId)
+                taskId, isVisible, displayId)
             logD("VisibleTaskCount has changed from %d to %d", prevCount, newCount)
             notifyVisibleTaskListeners(displayId, newCount)
         }
@@ -350,7 +335,7 @@
      *
      * Unminimizes the task if it is minimized.
      */
-    fun addOrMoveFreeformTaskToTop(displayId: Int, taskId: Int) {
+    private fun addOrMoveFreeformTaskToTop(displayId: Int, taskId: Int) {
         logD("Add or move task to top: display=%d taskId=%d", taskId, displayId)
         desktopTaskDataByDisplayId[displayId]?.freeformTasksInZOrder?.remove(taskId)
         desktopTaskDataByDisplayId.getOrCreate(displayId).freeformTasksInZOrder.add(0, taskId)
@@ -373,7 +358,7 @@
             logD("Minimize Task: display=%d, task=%d", displayId, taskId)
             desktopTaskDataByDisplayId.getOrCreate(displayId).minimizedTasks.add(taskId)
         }
-
+        updateTask(displayId, taskId, isVisible = false)
         if (Flags.enableDesktopWindowingPersistence()) {
             updatePersistentRepository(displayId)
         }
@@ -421,7 +406,7 @@
         // Remove task from unminimized task if it is minimized.
         unminimizeTask(displayId, taskId)
         removeActiveTask(taskId)
-        updateTaskVisibility(displayId, taskId, visible = false)
+        removeVisibleTask(taskId)
         if (Flags.enableDesktopWindowingPersistence()) {
             updatePersistentRepository(displayId)
         }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTaskChangeListener.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTaskChangeListener.kt
index 1ee2de9..94ac2e6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTaskChangeListener.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTaskChangeListener.kt
@@ -17,21 +17,56 @@
 package com.android.wm.shell.desktopmode
 
 import android.app.ActivityManager.RunningTaskInfo
+import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
+import android.window.DesktopModeFlags
 import com.android.wm.shell.freeform.TaskChangeListener
 
 /** Manages tasks handling specific to Android Desktop Mode. */
-class DesktopTaskChangeListener: TaskChangeListener {
+class DesktopTaskChangeListener(
+    private val desktopRepository: DesktopRepository,
+) : TaskChangeListener {
 
   override fun onTaskOpening(taskInfo: RunningTaskInfo) {
-    // TODO: b/367268953 - Connect this with DesktopRepository.
+    if (!isFreeformTask(taskInfo) && desktopRepository.isActiveTask(taskInfo.taskId)) {
+      desktopRepository.removeFreeformTask(taskInfo.displayId, taskInfo.taskId)
+      return
+    }
+    if (isFreeformTask(taskInfo)) {
+      desktopRepository.addTask(taskInfo.displayId, taskInfo.taskId, taskInfo.isVisible)
+    }
   }
 
   override fun onTaskChanging(taskInfo: RunningTaskInfo) {
-    // TODO: b/367268953 - Connect this with DesktopRepository.
+    if (!desktopRepository.isActiveTask(taskInfo.taskId)) return
+
+    // Case 1: Freeform task is changed in Desktop Mode.
+    if (isFreeformTask(taskInfo)) {
+      if (taskInfo.isVisible) {
+        desktopRepository.addTask(taskInfo.displayId, taskInfo.taskId, taskInfo.isVisible)
+      }
+      desktopRepository.updateTask(
+          taskInfo.displayId, taskInfo.taskId, taskInfo.isVisible)
+    } else {
+      // Case 2: Freeform task is changed outside Desktop Mode.
+      desktopRepository.removeFreeformTask(taskInfo.displayId, taskInfo.taskId)
+    }
+  }
+
+  // This method should only be used for scenarios where the task info changes are not propagated to
+  // [DesktopTaskChangeListener#onTaskChanging] via [TransitionsObserver].
+  // Any changes to [DesktopRepository] from this method should be made carefully to minimize risk
+  // of race conditions and possible duplications with [onTaskChanging].
+  override fun onNonTransitionTaskChanging(taskInfo: RunningTaskInfo) {
+    // TODO: b/367268953 - Propapagate usages from FreeformTaskListener to this method.
   }
 
   override fun onTaskMovingToFront(taskInfo: RunningTaskInfo) {
-    // TODO: b/367268953 - Connect this with DesktopRepository.
+    if (!desktopRepository.isActiveTask(taskInfo.taskId)) return
+    if (!isFreeformTask(taskInfo)) {
+      desktopRepository.removeFreeformTask(taskInfo.displayId, taskInfo.taskId)
+    }
+    // TODO: b/367268953 - Connect this with DesktopRepository for handling
+    // task moving to front for tasks in windowing mode.
   }
 
   override fun onTaskMovingToBack(taskInfo: RunningTaskInfo) {
@@ -39,6 +74,20 @@
   }
 
   override fun onTaskClosing(taskInfo: RunningTaskInfo) {
-    // TODO: b/367268953 - Connect this with DesktopRepository.
+    if (!desktopRepository.isActiveTask(taskInfo.taskId)) return
+    // TODO: b/370038902 - Handle Activity#finishAndRemoveTask.
+    if (!DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION.isTrue() ||
+        desktopRepository.isClosingTask(taskInfo.taskId)) {
+      // A task that's vanishing should be removed:
+      // - If it's closed by the X button which means it's marked as a closing task.
+      desktopRepository.removeClosingTask(taskInfo.taskId)
+      desktopRepository.removeFreeformTask(taskInfo.displayId, taskInfo.taskId)
+    } else {
+      desktopRepository.updateTask(taskInfo.displayId, taskInfo.taskId, isVisible = false)
+      desktopRepository.minimizeTask(taskInfo.displayId, taskInfo.taskId)
+    }
   }
+
+  private fun isFreeformTask(taskInfo: RunningTaskInfo): Boolean =
+      taskInfo.windowingMode == WINDOWING_MODE_FREEFORM
 }
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 77fb4b4..577c18c 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
@@ -87,6 +87,7 @@
 import com.android.wm.shell.common.SingleInstanceRemoteListener
 import com.android.wm.shell.common.SyncTransactionQueue
 import com.android.wm.shell.compatui.isTopActivityExemptFromDesktopWindowing
+import com.android.wm.shell.desktopmode.DesktopImmersiveController.ExitResult
 import com.android.wm.shell.desktopmode.DesktopModeVisualIndicator.DragStartState
 import com.android.wm.shell.desktopmode.DesktopModeVisualIndicator.IndicatorType
 import com.android.wm.shell.desktopmode.DesktopRepository.VisibleTasksListener
@@ -118,6 +119,7 @@
 import com.android.wm.shell.transition.OneShotRemoteHandler
 import com.android.wm.shell.transition.Transitions
 import com.android.wm.shell.transition.Transitions.TransitionFinishCallback
+import com.android.wm.shell.windowdecor.DesktopModeWindowDecoration
 import com.android.wm.shell.windowdecor.DragPositioningCallbackUtility
 import com.android.wm.shell.windowdecor.MoveToDesktopAnimator
 import com.android.wm.shell.windowdecor.OnTaskRepositionAnimationListener
@@ -125,6 +127,7 @@
 import com.android.wm.shell.windowdecor.extension.isFullscreen
 import com.android.wm.shell.windowdecor.extension.isMultiWindow
 import com.android.wm.shell.windowdecor.extension.requestingImmersive
+import com.android.wm.shell.windowdecor.tiling.DesktopTilingDecorViewModel
 import java.io.PrintWriter
 import java.util.Optional
 import java.util.concurrent.Executor
@@ -144,6 +147,7 @@
     private val transitions: Transitions,
     private val keyguardManager: KeyguardManager,
     private val returnToDragStartAnimator: ReturnToDragStartAnimator,
+    private val desktopMixedTransitionHandler: DesktopMixedTransitionHandler,
     private val enterDesktopTaskTransitionHandler: EnterDesktopTaskTransitionHandler,
     private val exitDesktopTaskTransitionHandler: ExitDesktopTaskTransitionHandler,
     private val desktopModeDragAndDropTransitionHandler: DesktopModeDragAndDropTransitionHandler,
@@ -163,6 +167,7 @@
     private val inputManager: InputManager,
     private val focusTransitionObserver: FocusTransitionObserver,
     private val desktopModeEventLogger: DesktopModeEventLogger,
+    private val desktopTilingDecorViewModel: DesktopTilingDecorViewModel,
 ) :
     RemoteCallable<DesktopTasksController>,
     Transitions.TransitionHandler,
@@ -237,6 +242,7 @@
                 override fun onAnimationStateChanged(running: Boolean) {
                     logV("Recents animation state changed running=%b", running)
                     recentsAnimationRunning = running
+                    desktopTilingDecorViewModel.onOverviewAnimationStateChange(running)
                 }
             }
         )
@@ -373,9 +379,9 @@
         }
         logV("moveBackgroundTaskToDesktop with taskId=%d", taskId)
         // TODO(342378842): Instead of using default display, support multiple displays
-        val taskToMinimize = bringDesktopAppsToFrontBeforeShowingNewTask(
+        val taskIdToMinimize = bringDesktopAppsToFrontBeforeShowingNewTask(
             DEFAULT_DISPLAY, wct, taskId)
-        val runOnTransit = desktopImmersiveController.exitImmersiveIfApplicable(
+        val exitResult = desktopImmersiveController.exitImmersiveIfApplicable(
             wct = wct,
             displayId = DEFAULT_DISPLAY,
             excludeTaskId = taskId,
@@ -388,8 +394,8 @@
         )
         // TODO(343149901): Add DPI changes for task launch
         val transition = enterDesktopTaskTransitionHandler.moveToDesktop(wct, transitionSource)
-        addPendingMinimizeTransition(transition, taskToMinimize)
-        runOnTransit?.invoke(transition)
+        taskIdToMinimize?.let { addPendingMinimizeTransition(transition, it) }
+        exitResult.asExit()?.runOnTransitionStart?.invoke(transition)
         return true
     }
 
@@ -406,19 +412,19 @@
         }
         logV("moveRunningTaskToDesktop taskId=%d", task.taskId)
         exitSplitIfApplicable(wct, task)
-        val runOnTransit = desktopImmersiveController.exitImmersiveIfApplicable(
+        val exitResult = desktopImmersiveController.exitImmersiveIfApplicable(
             wct = wct,
             displayId = task.displayId,
             excludeTaskId = task.taskId,
         )
         // Bring other apps to front first
-        val taskToMinimize =
+        val taskIdToMinimize =
             bringDesktopAppsToFrontBeforeShowingNewTask(task.displayId, wct, task.taskId)
         addMoveToDesktopChanges(wct, task)
 
         val transition = enterDesktopTaskTransitionHandler.moveToDesktop(wct, transitionSource)
-        addPendingMinimizeTransition(transition, taskToMinimize)
-        runOnTransit?.invoke(transition)
+        taskIdToMinimize?.let { addPendingMinimizeTransition(transition, it) }
+        exitResult.asExit()?.runOnTransitionStart?.invoke(transition)
     }
 
     /**
@@ -452,15 +458,15 @@
         val wct = WindowContainerTransaction()
         exitSplitIfApplicable(wct, taskInfo)
         moveHomeTask(wct, toTop = true)
-        val taskToMinimize =
+        val taskIdToMinimize =
             bringDesktopAppsToFrontBeforeShowingNewTask(taskInfo.displayId, wct, taskInfo.taskId)
         addMoveToDesktopChanges(wct, taskInfo)
-        val runOnTransit = desktopImmersiveController.exitImmersiveIfApplicable(
+        val exitResult = desktopImmersiveController.exitImmersiveIfApplicable(
             wct, taskInfo.displayId)
         val transition = dragToDesktopTransitionHandler.finishDragToDesktopTransition(wct)
         transition?.let {
-            addPendingMinimizeTransition(it, taskToMinimize)
-            runOnTransit?.invoke(transition)
+            taskIdToMinimize?.let { taskId -> addPendingMinimizeTransition(it, taskId) }
+            exitResult.asExit()?.runOnTransitionStart?.invoke(transition)
         }
     }
 
@@ -492,6 +498,7 @@
         taskInfo: RunningTaskInfo,
     ): ((IBinder) -> Unit)? {
         val taskId = taskInfo.taskId
+        desktopTilingDecorViewModel.removeTaskIfTiled(displayId, taskId)
         if (taskRepository.isOnlyVisibleNonClosingTask(taskId)) {
             removeWallpaperActivity(wct)
         }
@@ -502,7 +509,8 @@
                 taskId
             )
         )
-        return desktopImmersiveController.exitImmersiveIfApplicable(wct, taskInfo)
+        return desktopImmersiveController.exitImmersiveIfApplicable(wct, taskInfo).asExit()
+            ?.runOnTransitionStart
     }
 
     fun minimizeTask(taskInfo: RunningTaskInfo) {
@@ -515,7 +523,7 @@
             removeWallpaperActivity(wct)
         }
         // Notify immersive handler as it might need to exit immersive state.
-        val runOnTransit = desktopImmersiveController.exitImmersiveIfApplicable(wct, taskInfo)
+        val exitResult = desktopImmersiveController.exitImmersiveIfApplicable(wct, taskInfo)
 
         wct.reorder(taskInfo.token, false)
         val transition = freeformTaskTransitionStarter.startMinimizedModeTransition(wct)
@@ -526,12 +534,13 @@
                 taskId = taskId
             )
         }
-        runOnTransit?.invoke(transition)
+        exitResult.asExit()?.runOnTransitionStart?.invoke(transition)
     }
 
     /** Move a task with given `taskId` to fullscreen */
     fun moveToFullscreen(taskId: Int, transitionSource: DesktopModeTransitionSource) {
         shellTaskOrganizer.getRunningTaskInfo(taskId)?.let { task ->
+            desktopTilingDecorViewModel.removeTaskIfTiled(task.displayId, taskId)
             moveToFullscreenWithAnimation(task, task.positionInParent, transitionSource)
         }
     }
@@ -539,6 +548,7 @@
     /** Enter fullscreen by moving the focused freeform task in given `displayId` to fullscreen. */
     fun enterFullscreen(displayId: Int, transitionSource: DesktopModeTransitionSource) {
         getFocusedFreeformTask(displayId)?.let {
+            desktopTilingDecorViewModel.removeTaskIfTiled(displayId, it.taskId)
             moveToFullscreenWithAnimation(it, it.positionInParent, transitionSource)
         }
     }
@@ -546,6 +556,7 @@
     /** Move a desktop app to split screen. */
     fun moveToSplit(task: RunningTaskInfo) {
         logV( "moveToSplit taskId=%s", task.taskId)
+        desktopTilingDecorViewModel.removeTaskIfTiled(task.displayId, task.taskId)
         val wct = WindowContainerTransaction()
         wct.setBounds(task.token, Rect())
         // Rather than set windowing mode to multi-window at task level, set it to
@@ -619,7 +630,7 @@
         logV("moveBackgroundTaskToFront taskId=%s", taskId)
         val wct = WindowContainerTransaction()
         // TODO: b/342378842 - Instead of using default display, support multiple displays
-        val runOnTransit = desktopImmersiveController.exitImmersiveIfApplicable(
+        val exitResult = desktopImmersiveController.exitImmersiveIfApplicable(
             wct = wct,
             displayId = DEFAULT_DISPLAY,
             excludeTaskId = taskId,
@@ -630,8 +641,13 @@
                 launchWindowingMode = WINDOWING_MODE_FREEFORM
             }.toBundle(),
         )
-        val transition = startLaunchTransition(TRANSIT_OPEN, wct, taskId, remoteTransition)
-        runOnTransit?.invoke(transition)
+        val transition = startLaunchTransition(
+            TRANSIT_OPEN,
+            wct,
+            taskId,
+            remoteTransition = remoteTransition
+        )
+        exitResult.asExit()?.runOnTransitionStart?.invoke(transition)
     }
 
     /**
@@ -643,32 +659,50 @@
     @JvmOverloads
     fun moveTaskToFront(taskInfo: RunningTaskInfo, remoteTransition: RemoteTransition? = null) {
         logV("moveTaskToFront taskId=%s", taskInfo.taskId)
+        // If a task is tiled, another task should be brought to foreground with it so let
+        // tiling controller handle the request.
+        if (desktopTilingDecorViewModel.moveTaskToFrontIfTiled(taskInfo)) {
+            return
+        }
         val wct = WindowContainerTransaction()
         wct.reorder(taskInfo.token, true /* onTop */, true /* includingParents */)
-        val runOnTransit = desktopImmersiveController.exitImmersiveIfApplicable(
+        val result = desktopImmersiveController.exitImmersiveIfApplicable(
             wct = wct,
             displayId = taskInfo.displayId,
             excludeTaskId = taskInfo.taskId,
         )
-        val transition =
-            startLaunchTransition(TRANSIT_TO_FRONT, wct, taskInfo.taskId, remoteTransition)
-        runOnTransit?.invoke(transition)
+        val exitResult = if (result is ExitResult.Exit) { result } else { null }
+        val transition = startLaunchTransition(
+            transitionType = TRANSIT_TO_FRONT,
+            wct = wct,
+            taskId = taskInfo.taskId,
+            exitingImmersiveTask = exitResult?.exitingTask,
+            remoteTransition = remoteTransition,
+            displayId = taskInfo.displayId,
+        )
+        exitResult?.runOnTransitionStart?.invoke(transition)
     }
 
     private fun startLaunchTransition(
         transitionType: Int,
         wct: WindowContainerTransaction,
         taskId: Int,
-        remoteTransition: RemoteTransition?,
+        exitingImmersiveTask: Int? = null,
+        remoteTransition: RemoteTransition? = null,
+        displayId: Int = DEFAULT_DISPLAY,
     ): IBinder {
-        val taskToMinimize: RunningTaskInfo? =
-            addAndGetMinimizeChanges(DEFAULT_DISPLAY, wct, taskId)
+        val taskIdToMinimize = addAndGetMinimizeChanges(displayId, wct, taskId)
         if (remoteTransition == null) {
-            val t = transitions.startTransition(transitionType, wct, null /* handler */)
-            addPendingMinimizeTransition(t, taskToMinimize)
+            val t = desktopMixedTransitionHandler.startLaunchTransition(
+                transitionType = transitionType,
+                wct = wct,
+                taskId = taskId,
+                exitingImmersiveTask = exitingImmersiveTask,
+            )
+            taskIdToMinimize?.let { addPendingMinimizeTransition(t, it) }
             return t
         }
-        if (taskToMinimize == null) {
+        if (taskIdToMinimize == null) {
             val remoteTransitionHandler = OneShotRemoteHandler(mainExecutor, remoteTransition)
             val t = transitions.startTransition(transitionType, wct, remoteTransitionHandler)
             remoteTransitionHandler.setTransition(t)
@@ -676,10 +710,10 @@
         }
         val remoteTransitionHandler =
             DesktopWindowLimitRemoteHandler(
-                mainExecutor, rootTaskDisplayAreaOrganizer, remoteTransition, taskToMinimize.taskId)
+                mainExecutor, rootTaskDisplayAreaOrganizer, remoteTransition, taskIdToMinimize)
         val t = transitions.startTransition(transitionType, wct, remoteTransitionHandler)
         remoteTransitionHandler.setTransition(t)
-        addPendingMinimizeTransition(t, taskToMinimize)
+        taskIdToMinimize.let { addPendingMinimizeTransition(t, it) }
         return t
     }
 
@@ -798,13 +832,13 @@
         } else {
             // Save current bounds so that task can be restored back to original bounds if necessary
             // and toggle to the stable bounds.
+            desktopTilingDecorViewModel.removeTaskIfTiled(taskInfo.displayId, taskInfo.taskId)
             taskRepository.saveBoundsBeforeMaximize(taskInfo.taskId, currentTaskBounds)
 
             destinationBounds.set(calculateMaximizeBounds(displayLayout, taskInfo))
         }
 
 
-
         val shouldRestoreToSnap =
             isMaximized && isTaskSnappedToHalfScreen(taskInfo, destinationBounds)
 
@@ -823,6 +857,39 @@
         toggleResizeDesktopTaskTransitionHandler.startTransition(wct)
     }
 
+    private fun dragToMaximizeDesktopTask(
+        taskInfo: RunningTaskInfo,
+        taskSurface: SurfaceControl,
+        currentDragBounds: Rect,
+        motionEvent: MotionEvent
+    ) {
+        val displayLayout = displayController.getDisplayLayout(taskInfo.displayId) ?: return
+        val stableBounds = Rect()
+        displayLayout.getStableBounds(stableBounds)
+        if (isTaskMaximized(taskInfo, stableBounds)) {
+            // Handle the case where we attempt to drag-to-maximize when already maximized: the task
+            // position won't need to change but we want to animate the surface going back to the
+            // maximized position.
+            val containerBounds = taskInfo.configuration.windowConfiguration.bounds
+            if (containerBounds != currentDragBounds) {
+                returnToDragStartAnimator.start(
+                    taskInfo.taskId,
+                    taskSurface,
+                    startBounds = currentDragBounds,
+                    endBounds = containerBounds,
+                    isResizable = taskInfo.isResizeable
+                )
+            }
+            return
+        }
+
+        // TODO(b/375356605): Introduce a new ResizeTrigger for drag-to-top.
+        desktopModeEventLogger.logTaskResizingStarted(
+            ResizeTrigger.UNKNOWN_RESIZE_TRIGGER, motionEvent, taskInfo, displayController
+        )
+        toggleDesktopTaskSize(taskInfo, ResizeTrigger.UNKNOWN_RESIZE_TRIGGER, motionEvent)
+    }
+
     private fun getMaximizeBounds(taskInfo: RunningTaskInfo, stableBounds: Rect): Rect {
         if (taskInfo.isResizeable) {
             // if resizable then expand to entire stable bounds (full display minus insets)
@@ -912,7 +979,20 @@
         position: SnapPosition,
         resizeTrigger: ResizeTrigger,
         motionEvent: MotionEvent?,
+        desktopWindowDecoration: DesktopModeWindowDecoration,
     ) {
+        if (DesktopModeFlags.ENABLE_TILE_RESIZING.isTrue()) {
+            val isTiled = desktopTilingDecorViewModel.snapToHalfScreen(
+                taskInfo,
+                desktopWindowDecoration,
+                position,
+                currentDragBounds,
+            )
+            if (isTiled) {
+                taskbarDesktopTaskListener?.onTaskbarCornerRoundingUpdate(true)
+            }
+            return
+        }
         val destinationBounds = getSnapBounds(taskInfo, position)
         desktopModeEventLogger.logTaskResizingEnded(
             resizeTrigger,
@@ -932,7 +1012,7 @@
                     taskSurface,
                     startBounds = currentDragBounds,
                     endBounds = destinationBounds,
-                    isResizable = taskInfo.isResizeable
+                    isResizable = taskInfo.isResizeable,
                 )
             }
             return
@@ -952,6 +1032,7 @@
         currentDragBounds: Rect,
         dragStartBounds: Rect,
         motionEvent: MotionEvent,
+        desktopModeWindowDecoration: DesktopModeWindowDecoration,
     ) {
         releaseVisualIndicator()
         if (!taskInfo.isResizeable && DISABLE_NON_RESIZABLE_APP_SNAP_RESIZE.isTrue()) {
@@ -986,6 +1067,7 @@
                 position,
                 resizeTrigger,
                 motionEvent,
+                desktopModeWindowDecoration,
             )
         }
     }
@@ -1032,13 +1114,13 @@
         displayId: Int,
         wct: WindowContainerTransaction,
         newTaskIdInFront: Int
-    ): RunningTaskInfo? = bringDesktopAppsToFront(displayId, wct, newTaskIdInFront)
+    ): Int? = bringDesktopAppsToFront(displayId, wct, newTaskIdInFront)
 
     private fun bringDesktopAppsToFront(
         displayId: Int,
         wct: WindowContainerTransaction,
         newTaskIdInFront: Int? = null
-    ): RunningTaskInfo? {
+    ): Int? {
         logV("bringDesktopAppsToFront, newTaskId=%d", newTaskIdInFront)
         // Move home to front, ensures that we go back home when all desktop windows are closed
         moveHomeTask(wct, toTop = true)
@@ -1054,11 +1136,10 @@
             taskRepository.getExpandedTasksOrdered(displayId)
         // If we're adding a new Task we might need to minimize an old one
         // TODO(b/365725441): Handle non running task minimization
-        val taskToMinimize: RunningTaskInfo? =
+        val taskIdToMinimize: Int? =
             if (newTaskIdInFront != null && desktopTasksLimiter.isPresent) {
-                desktopTasksLimiter
-                    .get()
-                    .getTaskToMinimize(
+                desktopTasksLimiter.get()
+                    .getTaskIdToMinimize(
                         expandedTasksOrderedFrontToBack,
                         newTaskIdInFront
                     )
@@ -1068,7 +1149,7 @@
 
         expandedTasksOrderedFrontToBack
             // If there is a Task to minimize, let it stay behind the Home Task
-            .filter { taskId -> taskId != taskToMinimize?.taskId }
+            .filter { taskId -> taskId != taskIdToMinimize }
             .reversed() // Start from the back so the front task is brought forward last
             .forEach { taskId ->
                 val runningTaskInfo = shellTaskOrganizer.getRunningTaskInfo(taskId)
@@ -1089,7 +1170,7 @@
         taskbarDesktopTaskListener?.
             onTaskbarCornerRoundingUpdate(doesAnyTaskRequireTaskbarRounding(displayId))
 
-        return taskToMinimize
+        return taskIdToMinimize
     }
 
     private fun moveHomeTask(wct: WindowContainerTransaction, toTop: Boolean) {
@@ -1217,10 +1298,7 @@
                     // Check if freeform task launch during recents should be handled
                     shouldHandleMidRecentsFreeformLaunch -> handleMidRecentsFreeformTaskLaunch(task)
                     // Check if the closing task needs to be handled
-                    TransitionUtil.isClosingType(request.type) -> handleTaskClosing(
-                        task,
-                        request.type
-                    )
+                    TransitionUtil.isClosingType(request.type) -> handleTaskClosing(task)
                     // Check if the top task shouldn't be allowed to enter desktop mode
                     isIncompatibleTask(task) -> handleIncompatibleTaskLaunch(task)
                     // Check if fullscreen task should be updated
@@ -1341,16 +1419,17 @@
         val options = createNewWindowOptions(callingTask)
         if (options.launchWindowingMode == WINDOWING_MODE_FREEFORM) {
             wct.startTask(requestedTaskId, options.toBundle())
-            val taskToMinimize = bringDesktopAppsToFrontBeforeShowingNewTask(
+            val taskIdToMinimize = bringDesktopAppsToFrontBeforeShowingNewTask(
                 callingTask.displayId, wct, requestedTaskId)
-            val runOnTransit = desktopImmersiveController.exitImmersiveIfApplicable(
-                wct = wct,
-                displayId = callingTask.displayId,
-                excludeTaskId = requestedTaskId,
-            )
+            val exitResult = desktopImmersiveController
+                .exitImmersiveIfApplicable(
+                    wct = wct,
+                    displayId = callingTask.displayId,
+                    excludeTaskId = requestedTaskId,
+                )
             val transition = transitions.startTransition(TRANSIT_OPEN, wct, null)
-            addPendingMinimizeTransition(transition, taskToMinimize)
-            runOnTransit?.invoke(transition)
+            taskIdToMinimize?.let { addPendingMinimizeTransition(transition, it) }
+            exitResult.asExit()?.runOnTransitionStart?.invoke(transition)
         } else {
             val splitPosition = splitScreenController.determineNewInstancePosition(callingTask)
             splitScreenController.startTask(requestedTaskId, splitPosition,
@@ -1489,12 +1568,16 @@
         // 1) Exit immersive if needed.
         desktopImmersiveController.exitImmersiveIfApplicable(transition, wct, task.displayId)
         // 2) minimize a Task if needed.
-        val taskToMinimize = addAndGetMinimizeChanges(task.displayId, wct, task.taskId)
-        if (taskToMinimize != null) {
-            addPendingMinimizeTransition(transition, taskToMinimize)
+        val taskIdToMinimize = addAndGetMinimizeChanges(task.displayId, wct, task.taskId)
+        if (taskIdToMinimize != null) {
+            addPendingMinimizeTransition(transition, taskIdToMinimize)
             return wct
         }
-        return if (wct.isEmpty) null else wct
+        if (!wct.isEmpty) {
+            desktopTilingDecorViewModel.removeTaskIfTiled(task.displayId, task.taskId)
+            return wct
+        }
+        return null
     }
 
     private fun handleFullscreenTaskLaunch(
@@ -1515,9 +1598,8 @@
 
                 // Desktop Mode is already showing and we're launching a new Task - we might need to
                 // minimize another Task.
-                val taskToMinimize =
-                    addAndGetMinimizeChanges(task.displayId, wct, task.taskId)
-                addPendingMinimizeTransition(transition, taskToMinimize)
+                val taskIdToMinimize = addAndGetMinimizeChanges(task.displayId, wct, task.taskId)
+                taskIdToMinimize?.let { addPendingMinimizeTransition(transition, it) }
                 desktopImmersiveController.exitImmersiveIfApplicable(
                     transition, wct, task.displayId
                 )
@@ -1543,10 +1625,7 @@
     }
 
     /** Handle task closing by removing wallpaper activity if it's the last active task */
-    private fun handleTaskClosing(
-        task: RunningTaskInfo,
-        transitionType: Int
-    ): WindowContainerTransaction? {
+    private fun handleTaskClosing(task: RunningTaskInfo): WindowContainerTransaction? {
         logV("handleTaskClosing")
         if (!isDesktopModeShowing(task.displayId))
             return null
@@ -1561,12 +1640,13 @@
 
         if (!DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION.isTrue()) {
             taskRepository.addClosingTask(task.displayId, task.taskId)
+            desktopTilingDecorViewModel.removeTaskIfTiled(task.displayId, task.taskId)
         }
 
         taskbarDesktopTaskListener?.onTaskbarCornerRoundingUpdate(
             doesAnyTaskRequireTaskbarRounding(
                 task.displayId,
-                task.id
+                task.taskId
             )
         )
         return if (wct.isEmpty) null else wct
@@ -1683,7 +1763,7 @@
         displayId: Int,
         wct: WindowContainerTransaction,
         newTaskId: Int
-    ): RunningTaskInfo? {
+    ): Int? {
         if (!desktopTasksLimiter.isPresent) return null
         return desktopTasksLimiter
             .get()
@@ -1692,9 +1772,9 @@
 
     private fun addPendingMinimizeTransition(
         transition: IBinder,
-        taskToMinimize: RunningTaskInfo?
+        taskIdToMinimize: Int,
     ) {
-        if (taskToMinimize == null) return
+        val taskToMinimize = shellTaskOrganizer.getRunningTaskInfo(taskIdToMinimize) ?: return
         desktopTasksLimiter.ifPresent {
             it.addPendingMinimizeChange(transition, taskToMinimize.displayId, taskToMinimize.taskId)
         }
@@ -1808,6 +1888,7 @@
         taskBounds: Rect
     ) {
         if (taskInfo.windowingMode != WINDOWING_MODE_FREEFORM) return
+        desktopTilingDecorViewModel.removeTaskIfTiled(taskInfo.displayId, taskInfo.taskId)
         updateVisualIndicator(taskInfo, taskSurface, inputX, taskBounds.top.toFloat(),
             DragStartState.FROM_FREEFORM)
     }
@@ -1857,6 +1938,7 @@
         validDragArea: Rect,
         dragStartBounds: Rect,
         motionEvent: MotionEvent,
+        desktopModeWindowDecoration: DesktopModeWindowDecoration,
     ) {
         if (taskInfo.configuration.windowConfiguration.windowingMode != WINDOWING_MODE_FREEFORM) {
             return
@@ -1869,11 +1951,15 @@
             )
         when (indicatorType) {
             IndicatorType.TO_FULLSCREEN_INDICATOR -> {
-                moveToFullscreenWithAnimation(
-                    taskInfo,
-                    position,
-                    DesktopModeTransitionSource.TASK_DRAG
-                )
+                if (DesktopModeStatus.shouldMaximizeWhenDragToTopEdge(context)) {
+                    dragToMaximizeDesktopTask(taskInfo, taskSurface, currentDragBounds, motionEvent)
+                } else {
+                    moveToFullscreenWithAnimation(
+                        taskInfo,
+                        position,
+                        DesktopModeTransitionSource.TASK_DRAG
+                    )
+                }
             }
             IndicatorType.TO_SPLIT_LEFT_INDICATOR -> {
                 handleSnapResizingTask(
@@ -1883,6 +1969,7 @@
                     currentDragBounds,
                     dragStartBounds,
                     motionEvent,
+                    desktopModeWindowDecoration,
                 )
             }
             IndicatorType.TO_SPLIT_RIGHT_INDICATOR -> {
@@ -1893,6 +1980,7 @@
                     currentDragBounds,
                     dragStartBounds,
                     motionEvent,
+                    desktopModeWindowDecoration,
                 )
             }
             IndicatorType.NO_INDICATOR -> {
@@ -2086,6 +2174,7 @@
     // TODO(b/366397912): Support full multi-user mode in Windowing.
     override fun onUserChanged(newUserId: Int, userContext: Context) {
         userId = newUserId
+        desktopTilingDecorViewModel.onUserChange()
     }
 
     /** Called when a task's info changes. */
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 cd28a4f..7bcc5d1 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
@@ -16,7 +16,6 @@
 
 package com.android.wm.shell.desktopmode
 
-import android.app.ActivityManager.RunningTaskInfo
 import android.content.Context
 import android.os.Handler
 import android.os.IBinder
@@ -190,17 +189,19 @@
             displayId: Int,
             wct: WindowContainerTransaction,
             newFrontTaskId: Int,
-    ): RunningTaskInfo? {
+    ): Int? {
         logV("addAndGetMinimizeTaskChanges, newFrontTask=%d", newFrontTaskId)
-        // This list is ordered from front to back.
-        val newTaskOrderedList = createOrderedTaskListWithNewTask(
-            taskRepository.getExpandedTasksOrdered(displayId), newFrontTaskId)
-        val taskToMinimize = getTaskToMinimize(newTaskOrderedList)
-        if (taskToMinimize != null) {
-            wct.reorder(taskToMinimize.token, false /* onTop */)
-            return taskToMinimize
+
+        val taskIdToMinimize =
+            getTaskIdToMinimize(
+                taskRepository.getExpandedTasksOrdered(displayId),
+                newFrontTaskId
+            )
+        // If it's a running task, reorder it to back.
+        taskIdToMinimize?.let { shellTaskOrganizer.getRunningTaskInfo(it) }?.let {
+            wct.reorder(it.token, false /* onTop */)
         }
-        return null
+        return taskIdToMinimize
     }
 
     /**
@@ -216,31 +217,33 @@
      * Returns the minimized task from the list of visible tasks ordered from front to back with
      * the new task placed in front of other tasks.
      */
-    fun getTaskToMinimize(
+    fun getTaskIdToMinimize(
             visibleOrderedTasks: List<Int>,
-            newTaskIdInFront: Int
-    ): RunningTaskInfo? =
-        getTaskToMinimize(createOrderedTaskListWithNewTask(visibleOrderedTasks, newTaskIdInFront))
-
-    /** Returns the Task to minimize given a list of visible tasks ordered from front to back. */
-    fun getTaskToMinimize(visibleOrderedTasks: List<Int>): RunningTaskInfo? {
-        if (visibleOrderedTasks.size <= maxTasksLimit) {
-            logV("No need to minimize; tasks below limit")
-            return null
-        }
-        val taskIdToMinimize = visibleOrderedTasks.last()
-        val taskToMinimize =
-                shellTaskOrganizer.getRunningTaskInfo(taskIdToMinimize)
-        if (taskToMinimize == null) {
-            logE("taskToMinimize(taskId = %d) == null", taskIdToMinimize)
-            return null
-        }
-        return taskToMinimize
+            newTaskIdInFront: Int? = null
+    ): Int? {
+        return getTaskIdToMinimize(
+            createOrderedTaskListWithGivenTaskInFront(
+                visibleOrderedTasks, newTaskIdInFront))
     }
 
-    private fun createOrderedTaskListWithNewTask(
-        orderedTaskIds: List<Int>, newTaskId: Int): List<Int> =
-            listOf(newTaskId) + orderedTaskIds.filter { taskId -> taskId != newTaskId }
+    /** Returns the Task to minimize given a list of visible tasks ordered from front to back. */
+    private fun getTaskIdToMinimize(visibleOrderedTasks: List<Int>): Int? {
+        if (visibleOrderedTasks.size <= maxTasksLimit) {
+            logV("No need to minimize; tasks below limit")
+            // No need to minimize anything
+            return null
+        }
+        return visibleOrderedTasks.last()
+    }
+
+    private fun createOrderedTaskListWithGivenTaskInFront(
+            existingTaskIdsOrderedFrontToBack: List<Int>,
+            newTaskId: Int?
+    ): List<Int> {
+        return if (newTaskId == null) existingTaskIdsOrderedFrontToBack
+        else listOf(newTaskId) +
+                existingTaskIdsOrderedFrontToBack.filter { taskId -> taskId != newTaskId }
+    }
 
     @VisibleForTesting
     fun getTransitionObserver(): TransitionObserver = minimizeTransitionObserver
diff --git a/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodRunnerState.java b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/persistence/DesktopRepositoryInitializer.kt
similarity index 69%
copy from ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodRunnerState.java
copy to libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/persistence/DesktopRepositoryInitializer.kt
index 83cbc52..771c3d1 100644
--- a/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodRunnerState.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/persistence/DesktopRepositoryInitializer.kt
@@ -13,10 +13,12 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package android.platform.test.ravenwood;
 
-/** Stub class. The actual implementaetion is in junit-impl-src. */
-public class RavenwoodRunnerState {
-    public RavenwoodRunnerState(RavenwoodAwareTestRunner runner) {
-    }
+package com.android.wm.shell.desktopmode.persistence
+
+import com.android.wm.shell.desktopmode.DesktopRepository
+
+/** Interface for initializing the [DesktopRepository]. */
+fun interface DesktopRepositoryInitializer {
+    fun initialize(repository: DesktopRepository)
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/persistence/DesktopRepositoryInitializerImpl.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/persistence/DesktopRepositoryInitializerImpl.kt
new file mode 100644
index 0000000..fc4ed15
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/persistence/DesktopRepositoryInitializerImpl.kt
@@ -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.wm.shell.desktopmode.persistence
+
+import android.content.Context
+import com.android.window.flags.Flags
+import com.android.wm.shell.desktopmode.DesktopRepository
+import com.android.wm.shell.shared.annotations.ShellMainThread
+import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
+
+/**
+ * Initializes the [DesktopRepository] from the [DesktopPersistentRepository].
+ *
+ * This class is responsible for reading the [DesktopPersistentRepository] and initializing the
+ * [DesktopRepository] with the tasks that previously existed in desktop.
+ */
+class DesktopRepositoryInitializerImpl(
+    private val context: Context,
+    private val persistentRepository: DesktopPersistentRepository,
+    @ShellMainThread private val mainCoroutineScope: CoroutineScope,
+) : DesktopRepositoryInitializer {
+    override fun initialize(repository: DesktopRepository) {
+        if (!Flags.enableDesktopWindowingPersistence()) return
+        //  TODO: b/365962554 - Handle the case that user moves to desktop before it's initialized
+        mainCoroutineScope.launch {
+            val desktop = persistentRepository.readDesktop() ?: return@launch
+
+            val maxTasks =
+                DesktopModeStatus.getMaxTaskLimit(context).takeIf { it > 0 }
+                    ?: desktop.zOrderedTasksCount
+
+            var visibleTasksCount = 0
+            desktop.zOrderedTasksList
+                // Reverse it so we initialize the repo from bottom to top.
+                .reversed()
+                .mapNotNull { taskId -> desktop.tasksByTaskIdMap[taskId] }
+                .forEach { task ->
+                    if (task.desktopTaskState == DesktopTaskState.VISIBLE
+                        && visibleTasksCount < maxTasks
+                    ) {
+                        visibleTasksCount++
+                        repository.addTask(desktop.displayId, task.taskId, isVisible = false)
+                    } else {
+                        repository.addTask(desktop.displayId, task.taskId, isVisible = false)
+                        repository.minimizeTask(desktop.displayId, task.taskId)
+                    }
+                }
+        }
+    }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragUtils.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragUtils.java
index 22cfa32..248a112 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragUtils.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragUtils.java
@@ -125,25 +125,35 @@
         StringJoiner str = new StringJoiner("|");
         if ((dragFlags & DRAG_FLAG_GLOBAL) != 0) {
             str.add("GLOBAL");
-        } else if ((dragFlags & DRAG_FLAG_GLOBAL_URI_READ) != 0) {
+        }
+        if ((dragFlags & DRAG_FLAG_GLOBAL_URI_READ) != 0) {
             str.add("GLOBAL_URI_READ");
-        } else if ((dragFlags & DRAG_FLAG_GLOBAL_URI_WRITE) != 0) {
+        }
+        if ((dragFlags & DRAG_FLAG_GLOBAL_URI_WRITE) != 0) {
             str.add("GLOBAL_URI_WRITE");
-        } else if ((dragFlags & DRAG_FLAG_GLOBAL_PERSISTABLE_URI_PERMISSION) != 0) {
+        }
+        if ((dragFlags & DRAG_FLAG_GLOBAL_PERSISTABLE_URI_PERMISSION) != 0) {
             str.add("GLOBAL_PERSISTABLE_URI_PERMISSION");
-        } else if ((dragFlags & DRAG_FLAG_GLOBAL_PREFIX_URI_PERMISSION) != 0) {
+        }
+        if ((dragFlags & DRAG_FLAG_GLOBAL_PREFIX_URI_PERMISSION) != 0) {
             str.add("GLOBAL_PREFIX_URI_PERMISSION");
-        } else if ((dragFlags & DRAG_FLAG_OPAQUE) != 0) {
+        }
+        if ((dragFlags & DRAG_FLAG_OPAQUE) != 0) {
             str.add("OPAQUE");
-        } else if ((dragFlags & DRAG_FLAG_ACCESSIBILITY_ACTION) != 0) {
+        }
+        if ((dragFlags & DRAG_FLAG_ACCESSIBILITY_ACTION) != 0) {
             str.add("ACCESSIBILITY_ACTION");
-        } else if ((dragFlags & DRAG_FLAG_REQUEST_SURFACE_FOR_RETURN_ANIMATION) != 0) {
+        }
+        if ((dragFlags & DRAG_FLAG_REQUEST_SURFACE_FOR_RETURN_ANIMATION) != 0) {
             str.add("REQUEST_SURFACE_FOR_RETURN_ANIMATION");
-        } else if ((dragFlags & DRAG_FLAG_GLOBAL_SAME_APPLICATION) != 0) {
+        }
+        if ((dragFlags & DRAG_FLAG_GLOBAL_SAME_APPLICATION) != 0) {
             str.add("GLOBAL_SAME_APPLICATION");
-        } else if ((dragFlags & DRAG_FLAG_START_INTENT_SENDER_ON_UNHANDLED_DRAG) != 0) {
+        }
+        if ((dragFlags & DRAG_FLAG_START_INTENT_SENDER_ON_UNHANDLED_DRAG) != 0) {
             str.add("START_INTENT_SENDER_ON_UNHANDLED_DRAG");
-        } else if ((dragFlags & DRAG_FLAG_HIDE_CALLING_TASK_ON_DRAG_START) != 0) {
+        }
+        if ((dragFlags & DRAG_FLAG_HIDE_CALLING_TASK_ON_DRAG_START) != 0) {
             str.add("HIDE_CALLING_TASK_ON_DRAG_START");
         }
         return str.toString();
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 a16446ff..cd20d97 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
@@ -34,7 +34,6 @@
 import com.android.wm.shell.protolog.ShellProtoLogGroup;
 import com.android.wm.shell.shared.desktopmode.DesktopModeStatus;
 import com.android.wm.shell.sysui.ShellInit;
-import com.android.wm.shell.transition.Transitions;
 import com.android.wm.shell.windowdecor.WindowDecorViewModel;
 
 import java.io.PrintWriter;
@@ -54,14 +53,10 @@
     private final Optional<DesktopTasksController> mDesktopTasksController;
     private final WindowDecorViewModel mWindowDecorationViewModel;
     private final LaunchAdjacentController mLaunchAdjacentController;
+    private final Optional<TaskChangeListener> mTaskChangeListener;
 
     private final SparseArray<State> mTasks = new SparseArray<>();
 
-    private static class State {
-        RunningTaskInfo mTaskInfo;
-        SurfaceControl mLeash;
-    }
-
     public FreeformTaskListener(
             Context context,
             ShellInit shellInit,
@@ -69,13 +64,15 @@
             Optional<DesktopRepository> desktopRepository,
             Optional<DesktopTasksController> desktopTasksController,
             LaunchAdjacentController launchAdjacentController,
-            WindowDecorViewModel windowDecorationViewModel) {
+            WindowDecorViewModel windowDecorationViewModel,
+            Optional<TaskChangeListener> taskChangeListener) {
         mContext = context;
         mShellTaskOrganizer = shellTaskOrganizer;
         mWindowDecorationViewModel = windowDecorationViewModel;
         mDesktopRepository = desktopRepository;
         mDesktopTasksController = desktopTasksController;
         mLaunchAdjacentController = launchAdjacentController;
+        mTaskChangeListener = taskChangeListener;
         if (shellInit != null) {
             shellInit.addInitCallback(this::onInit, this);
         }
@@ -100,14 +97,10 @@
         state.mLeash = leash;
         mTasks.put(taskInfo.taskId, state);
 
-        if (DesktopModeStatus.canEnterDesktopMode(mContext)) {
+        if (!DesktopModeFlags.ENABLE_WINDOWING_TRANSITION_HANDLERS_OBSERVERS.isTrue() &&
+                DesktopModeStatus.canEnterDesktopMode(mContext)) {
             mDesktopRepository.ifPresent(repository -> {
-                repository.addOrMoveFreeformTaskToTop(taskInfo.displayId, taskInfo.taskId);
-                if (taskInfo.isVisible) {
-                    repository.addActiveTask(taskInfo.displayId, taskInfo.taskId);
-                    repository.updateTaskVisibility(taskInfo.displayId, taskInfo.taskId,
-                        /* visible= */ true);
-                }
+                repository.addTask(taskInfo.displayId, taskInfo.taskId, taskInfo.isVisible);
             });
         }
         updateLaunchAdjacentController();
@@ -119,7 +112,8 @@
                 taskInfo.taskId);
         mTasks.remove(taskInfo.taskId);
 
-        if (DesktopModeStatus.canEnterDesktopMode(mContext)) {
+        if (!DesktopModeFlags.ENABLE_WINDOWING_TRANSITION_HANDLERS_OBSERVERS.isTrue() &&
+                DesktopModeStatus.canEnterDesktopMode(mContext)) {
             mDesktopRepository.ifPresent(repository -> {
                 // TODO: b/370038902 - Handle Activity#finishAndRemoveTask.
                 if (!DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION.isTrue()
@@ -129,7 +123,8 @@
                     repository.removeClosingTask(taskInfo.taskId);
                     repository.removeFreeformTask(taskInfo.displayId, taskInfo.taskId);
                 } else {
-                    repository.updateTaskVisibility(taskInfo.displayId, taskInfo.taskId, false);
+                    repository.updateTask(taskInfo.displayId, taskInfo.taskId, /* isVisible= */
+                            false);
                     repository.minimizeTask(taskInfo.displayId, taskInfo.taskId);
                 }
             });
@@ -148,13 +143,17 @@
         mWindowDecorationViewModel.onTaskInfoChanged(taskInfo);
         state.mTaskInfo = taskInfo;
         if (DesktopModeStatus.canEnterDesktopMode(mContext)) {
-            mDesktopRepository.ifPresent(repository -> {
-                if (taskInfo.isVisible) {
-                    repository.addActiveTask(taskInfo.displayId, taskInfo.taskId);
-                }
-                repository.updateTaskVisibility(taskInfo.displayId, taskInfo.taskId,
-                        taskInfo.isVisible);
-            });
+            if (DesktopModeFlags.ENABLE_WINDOWING_TRANSITION_HANDLERS_OBSERVERS.isTrue()) {
+                // Pass task info changes to the [TaskChangeListener] since [TransitionsObserver]
+                // does not propagate all task info changes.
+                mTaskChangeListener.ifPresent(listener ->
+                        listener.onNonTransitionTaskChanging(taskInfo));
+            } else {
+                mDesktopRepository.ifPresent(repository -> {
+                    repository.updateTask(taskInfo.displayId, taskInfo.taskId,
+                            taskInfo.isVisible);
+                });
+            }
         }
         updateLaunchAdjacentController();
     }
@@ -179,7 +178,7 @@
                 taskInfo.taskId, taskInfo.isFocused);
         if (DesktopModeStatus.canEnterDesktopMode(mContext) && taskInfo.isFocused) {
             mDesktopRepository.ifPresent(repository -> {
-                repository.addOrMoveFreeformTaskToTop(taskInfo.displayId, taskInfo.taskId);
+                repository.addTask(taskInfo.displayId, taskInfo.taskId, taskInfo.isVisible);
             });
         }
     }
@@ -213,4 +212,9 @@
     public String toString() {
         return TAG;
     }
+
+    private static class State {
+        RunningTaskInfo mTaskInfo;
+        SurfaceControl mLeash;
+    }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionHandler.java
index 58337ec..e848b88 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionHandler.java
@@ -27,6 +27,7 @@
 import android.graphics.Rect;
 import android.os.IBinder;
 import android.util.ArrayMap;
+import android.util.DisplayMetrics;
 import android.view.SurfaceControl;
 import android.view.WindowManager;
 import android.window.TransitionInfo;
@@ -38,6 +39,7 @@
 
 import com.android.wm.shell.common.DisplayController;
 import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.shared.animation.MinimizeAnimator;
 import com.android.wm.shell.transition.Transitions;
 
 import java.util.ArrayList;
@@ -137,7 +139,7 @@
                     break;
                 case WindowManager.TRANSIT_TO_BACK:
                     transitionHandled |= startMinimizeTransition(
-                            transition, info.getType(), change);
+                            transition, info.getType(), change, finishT, animations, onAnimFinish);
                     break;
                 case WindowManager.TRANSIT_CLOSE:
                     if (change.getTaskInfo().getWindowingMode() == WINDOWING_MODE_FREEFORM) {
@@ -206,7 +208,10 @@
     private boolean startMinimizeTransition(
             IBinder transition,
             int type,
-            TransitionInfo.Change change) {
+            TransitionInfo.Change change,
+            SurfaceControl.Transaction finishT,
+            ArrayList<Animator> animations,
+            Runnable onAnimFinish) {
         if (!mPendingTransitionTokens.contains(transition)) {
             return false;
         }
@@ -215,7 +220,23 @@
         if (type != Transitions.TRANSIT_MINIMIZE) {
             return false;
         }
-        // TODO(b/361524575): Add minimize animations
+
+        SurfaceControl.Transaction t = new SurfaceControl.Transaction();
+        SurfaceControl sc = change.getLeash();
+        finishT.hide(sc);
+        final DisplayMetrics displayMetrics =
+                mDisplayController
+                        .getDisplayContext(taskInfo.displayId).getResources().getDisplayMetrics();
+        final Animator animator = MinimizeAnimator.create(
+                displayMetrics,
+                change,
+                t,
+                (anim) -> {
+                    animations.remove(anim);
+                    onAnimFinish.run();
+                    return null;
+                });
+        animations.add(animator);
         return true;
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserver.java b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserver.java
index 7631ece..18f9cc7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserver.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserver.java
@@ -42,9 +42,9 @@
 import java.util.Optional;
 
 /**
- * The {@link Transitions.TransitionHandler} that handles freeform task launches, closes,
- * maximizing and restoring transitions. It also reports transitions so that window decorations can
- * be a part of transitions.
+ * The {@link Transitions.TransitionHandler} that handles freeform task launches, closes, maximizing
+ * and restoring transitions. It also reports transitions so that window decorations can be a part
+ * of transitions.
  */
 public class FreeformTaskTransitionObserver implements Transitions.TransitionObserver {
     private final Transitions mTransitions;
@@ -89,8 +89,8 @@
             // TODO(b/367268953): Remove when DesktopTaskListener is introduced and the repository
             //  is updated from there **before** the |mWindowDecorViewModel| methods are invoked.
             //  Otherwise window decoration relayout won't run with the immersive state up to date.
-            mDesktopImmersiveController.ifPresent(h ->
-                    h.onTransitionReady(transition, info, startT, finishT));
+            mDesktopImmersiveController.ifPresent(
+                    h -> h.onTransitionReady(transition, info, startT, finishT));
         }
         // Update focus state first to ensure the correct state can be queried from listeners.
         // TODO(371503964): Remove this once the unified task repository is ready.
@@ -147,33 +147,28 @@
             TransitionInfo.Change change,
             SurfaceControl.Transaction startT,
             SurfaceControl.Transaction finishT) {
-        mTaskChangeListener.ifPresent(
-            listener -> listener.onTaskOpening(change.getTaskInfo()));
+        mTaskChangeListener.ifPresent(listener -> listener.onTaskOpening(change.getTaskInfo()));
         mWindowDecorViewModel.onTaskOpening(
-            change.getTaskInfo(), change.getLeash(), startT, finishT);
+                change.getTaskInfo(), change.getLeash(), startT, finishT);
     }
 
     private void onCloseTransitionReady(
             TransitionInfo.Change change,
             SurfaceControl.Transaction startT,
             SurfaceControl.Transaction finishT) {
-        mTaskChangeListener.ifPresent(
-            listener -> listener.onTaskClosing(change.getTaskInfo()));
+        mTaskChangeListener.ifPresent(listener -> listener.onTaskClosing(change.getTaskInfo()));
         mWindowDecorViewModel.onTaskClosing(change.getTaskInfo(), startT, finishT);
-
     }
 
     private void onChangeTransitionReady(
             TransitionInfo.Change change,
             SurfaceControl.Transaction startT,
             SurfaceControl.Transaction finishT) {
-        mTaskChangeListener.ifPresent(listener ->
-            listener.onTaskChanging(change.getTaskInfo()));
+        mTaskChangeListener.ifPresent(listener -> listener.onTaskChanging(change.getTaskInfo()));
         mWindowDecorViewModel.onTaskChanging(
                 change.getTaskInfo(), change.getLeash(), startT, finishT);
     }
 
-
     private void onToFrontTransitionReady(
             TransitionInfo.Change change,
             SurfaceControl.Transaction startT,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionStarterInitializer.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionStarterInitializer.kt
index 98bdf05..c0613e2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionStarterInitializer.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionStarterInitializer.kt
@@ -28,7 +28,7 @@
 class FreeformTaskTransitionStarterInitializer(
     shellInit: ShellInit,
     private val windowDecorViewModel: WindowDecorViewModel,
-    private val freeformTaskTransitionStarter: FreeformTaskTransitionStarter
+    private val freeformTaskTransitionStarter: FreeformTaskTransitionStarter,
 ) {
     init {
         shellInit.addInitCallback(::onShellInit, this)
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/TaskChangeListener.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/TaskChangeListener.kt
index f07c069..aee92ea 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/TaskChangeListener.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/TaskChangeListener.kt
@@ -16,7 +16,7 @@
 
 package com.android.wm.shell.freeform
 
-import android.app.ActivityManager.RunningTaskInfo;
+import android.app.ActivityManager.RunningTaskInfo
 
 /**
  * Interface used by [FreeformTaskTransitionObserver] to manage freeform tasks.
@@ -27,9 +27,18 @@
     /** Notifies a task opening in freeform mode. */
     fun onTaskOpening(taskInfo: RunningTaskInfo)
 
-    /** Notifies a task info update on the given task. */
+    /** Notifies a task info update on the given task from Shell Transitions framework. */
     fun onTaskChanging(taskInfo: RunningTaskInfo)
 
+    /**
+     * Notifies a task info update on the given task from [FreeformTaskListener].
+     *
+     * This is used to propagate task info changes since not all task changes are propagated from
+     * [TransitionObserver] in [onTaskChanging]. It is recommended to use [onTaskChanging] instead
+     * of this method where possible.
+     */
+    fun onNonTransitionTaskChanging(taskInfo: RunningTaskInfo)
+
     /** Notifies a task moving to the front. */
     fun onTaskMovingToFront(taskInfo: RunningTaskInfo)
 
@@ -38,4 +47,4 @@
 
     /** Notifies a task is closing. */
     fun onTaskClosing(taskInfo: RunningTaskInfo)
-}
\ No newline at end of file
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
index 94b344f..5ffc64f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
@@ -368,10 +368,8 @@
 
     /**
      * Finish the current transition if possible.
-     *
-     * @param tx transaction to be applied with a potentially new draw after finishing.
      */
-    public void finishTransition(@Nullable SurfaceControl.Transaction tx) {
+    public void finishTransition() {
     }
 
     /**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java
index 89d3dd6..242f7fa 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java
@@ -15,7 +15,12 @@
  */
 package com.android.wm.shell.pip.phone;
 
+import static com.android.internal.policy.TaskResizingAlgorithm.CTRL_BOTTOM;
+import static com.android.internal.policy.TaskResizingAlgorithm.CTRL_LEFT;
 import static com.android.internal.policy.TaskResizingAlgorithm.CTRL_NONE;
+import static com.android.internal.policy.TaskResizingAlgorithm.CTRL_RIGHT;
+import static com.android.internal.policy.TaskResizingAlgorithm.CTRL_TOP;
+import static com.android.wm.shell.pip.phone.PipMenuView.ANIM_TYPE_NONE;
 
 import android.annotation.Nullable;
 import android.content.Context;
@@ -23,6 +28,7 @@
 import android.graphics.Point;
 import android.graphics.PointF;
 import android.graphics.Rect;
+import android.graphics.Region;
 import android.hardware.input.InputManager;
 import android.os.Looper;
 import android.view.BatchedInputEventReceiver;
@@ -36,6 +42,8 @@
 
 import androidx.annotation.VisibleForTesting;
 
+import com.android.internal.policy.TaskResizingAlgorithm;
+import com.android.window.flags.Flags;
 import com.android.wm.shell.R;
 import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.common.pip.PipBoundsAlgorithm;
@@ -48,6 +56,7 @@
 
 import java.io.PrintWriter;
 import java.util.function.Consumer;
+import java.util.function.Function;
 
 /**
  * Helper on top of PipTouchHandler that handles inputs OUTSIDE of the PIP window, which is used to
@@ -71,6 +80,7 @@
     private final PipPinchResizingAlgorithm mPinchResizingAlgorithm;
     private final int mDisplayId;
     private final ShellExecutor mMainExecutor;
+    private final Region mTmpRegion = new Region();
 
     private final PointF mDownPoint = new PointF();
     private final PointF mDownSecondPoint = new PointF();
@@ -81,15 +91,24 @@
     private final Rect mLastResizeBounds = new Rect();
     private final Rect mUserResizeBounds = new Rect();
     private final Rect mDownBounds = new Rect();
+    private final Rect mDragCornerSize = new Rect();
+    private final Rect mTmpTopLeftCorner = new Rect();
+    private final Rect mTmpTopRightCorner = new Rect();
+    private final Rect mTmpBottomLeftCorner = new Rect();
+    private final Rect mTmpBottomRightCorner = new Rect();
+    private final Rect mDisplayBounds = new Rect();
+    private final Function<Rect, Rect> mMovementBoundsSupplier;
     private final Runnable mUpdateMovementBoundsRunnable;
     private final Consumer<Rect> mUpdateResizeBoundsCallback;
 
+    private int mDelta;
     private float mTouchSlop;
 
     private boolean mAllowGesture;
     private boolean mIsAttached;
     private boolean mIsEnabled;
     private boolean mEnablePinchResize;
+    private boolean mEnableDragCornerResize;
     private boolean mIsSysUiStateValid;
     private boolean mThresholdCrossed;
     private boolean mOngoingPinchToResize = false;
@@ -113,7 +132,7 @@
             PipBoundsState pipBoundsState, PipMotionHelper motionHelper,
             PipTouchState pipTouchState, PipTaskOrganizer pipTaskOrganizer,
             PipDismissTargetHandler pipDismissTargetHandler,
-            Runnable updateMovementBoundsRunnable,
+            Function<Rect, Rect> movementBoundsSupplier, Runnable updateMovementBoundsRunnable,
             PipUiEventLogger pipUiEventLogger, PhonePipMenuController menuActivityController,
             ShellExecutor mainExecutor, @Nullable PipPerfHintController pipPerfHintController) {
         mContext = context;
@@ -126,6 +145,7 @@
         mPipTouchState = pipTouchState;
         mPipTaskOrganizer = pipTaskOrganizer;
         mPipDismissTargetHandler = pipDismissTargetHandler;
+        mMovementBoundsSupplier = movementBoundsSupplier;
         mUpdateMovementBoundsRunnable = updateMovementBoundsRunnable;
         mPhonePipMenuController = menuActivityController;
         mPipUiEventLogger = pipUiEventLogger;
@@ -161,9 +181,20 @@
     }
 
     private void reloadResources() {
+        final Resources res = mContext.getResources();
+        mDelta = res.getDimensionPixelSize(R.dimen.pip_resize_edge_size);
+        mEnableDragCornerResize = Flags.enableDesktopWindowingPip();
         mTouchSlop = ViewConfiguration.get(mContext).getScaledTouchSlop();
     }
 
+    private void resetDragCorners() {
+        mDragCornerSize.set(0, 0, mDelta, mDelta);
+        mTmpTopLeftCorner.set(mDragCornerSize);
+        mTmpTopRightCorner.set(mDragCornerSize);
+        mTmpBottomLeftCorner.set(mDragCornerSize);
+        mTmpBottomRightCorner.set(mDragCornerSize);
+    }
+
     private void disposeInputChannel() {
         if (mInputEventReceiver != null) {
             mInputEventReceiver.dispose();
@@ -211,7 +242,7 @@
 
     @VisibleForTesting
     void onInputEvent(InputEvent ev) {
-        if (!mEnablePinchResize) {
+        if (!mEnableDragCornerResize && !mEnablePinchResize) {
             // No need to handle anything if neither form of resizing is enabled.
             return;
         }
@@ -239,6 +270,8 @@
 
             if (mEnablePinchResize && mOngoingPinchToResize) {
                 onPinchResize(mv);
+            } else if (mEnableDragCornerResize) {
+                onDragCornerResize(mv);
             }
         }
     }
@@ -250,6 +283,48 @@
         return mCtrlType != CTRL_NONE || mOngoingPinchToResize;
     }
 
+    /**
+     * Check whether the current x,y coordinate is within the region in which drag-resize should
+     * start.
+     * This consists of 4 small squares on the 4 corners of the PIP window, a quarter of which
+     * overlaps with the PIP window while the rest goes outside of the PIP window.
+     *  _ _           _ _
+     * |_|_|_________|_|_|
+     * |_|_|         |_|_|
+     *   |     PIP     |
+     *   |   WINDOW    |
+     *  _|_           _|_
+     * |_|_|_________|_|_|
+     * |_|_|         |_|_|
+     */
+    public boolean isWithinDragResizeRegion(int x, int y) {
+        if (!mEnableDragCornerResize) {
+            return false;
+        }
+
+        final Rect currentPipBounds = mPipBoundsState.getBounds();
+        if (currentPipBounds == null) {
+            return false;
+        }
+        resetDragCorners();
+        mTmpTopLeftCorner.offset(currentPipBounds.left - mDelta / 2,
+                currentPipBounds.top - mDelta /  2);
+        mTmpTopRightCorner.offset(currentPipBounds.right - mDelta / 2,
+                currentPipBounds.top - mDelta /  2);
+        mTmpBottomLeftCorner.offset(currentPipBounds.left - mDelta / 2,
+                currentPipBounds.bottom - mDelta /  2);
+        mTmpBottomRightCorner.offset(currentPipBounds.right - mDelta / 2,
+                currentPipBounds.bottom - mDelta /  2);
+
+        mTmpRegion.setEmpty();
+        mTmpRegion.op(mTmpTopLeftCorner, Region.Op.UNION);
+        mTmpRegion.op(mTmpTopRightCorner, Region.Op.UNION);
+        mTmpRegion.op(mTmpBottomLeftCorner, Region.Op.UNION);
+        mTmpRegion.op(mTmpBottomRightCorner, Region.Op.UNION);
+
+        return mTmpRegion.contains(x, y);
+    }
+
     public boolean isUsingPinchToZoom() {
         return mEnablePinchResize;
     }
@@ -260,17 +335,62 @@
 
     public boolean willStartResizeGesture(MotionEvent ev) {
         if (isInValidSysUiState()) {
-            if (ev.getActionMasked() == MotionEvent.ACTION_POINTER_DOWN) {
-                if (mEnablePinchResize && ev.getPointerCount() == 2) {
-                    onPinchResize(ev);
-                    mOngoingPinchToResize = mAllowGesture;
-                    return mAllowGesture;
-                }
+            switch (ev.getActionMasked()) {
+                case MotionEvent.ACTION_DOWN:
+                    if (isWithinDragResizeRegion((int) ev.getRawX(), (int) ev.getRawY())) {
+                        return true;
+                    }
+                    break;
+
+                case MotionEvent.ACTION_POINTER_DOWN:
+                    if (mEnablePinchResize && ev.getPointerCount() == 2) {
+                        onPinchResize(ev);
+                        mOngoingPinchToResize = mAllowGesture;
+                        return mAllowGesture;
+                    }
+                    break;
+
+                default:
+                    break;
             }
         }
         return false;
     }
 
+    private void setCtrlType(int x, int y) {
+        final Rect currentPipBounds = mPipBoundsState.getBounds();
+
+        Rect movementBounds = mMovementBoundsSupplier.apply(currentPipBounds);
+
+        mDisplayBounds.set(movementBounds.left,
+                movementBounds.top,
+                movementBounds.right + currentPipBounds.width(),
+                movementBounds.bottom + currentPipBounds.height());
+
+        if (mTmpTopLeftCorner.contains(x, y) && currentPipBounds.top != mDisplayBounds.top
+                && currentPipBounds.left != mDisplayBounds.left) {
+            mCtrlType |= CTRL_LEFT;
+            mCtrlType |= CTRL_TOP;
+        }
+        if (mTmpTopRightCorner.contains(x, y) && currentPipBounds.top != mDisplayBounds.top
+                && currentPipBounds.right != mDisplayBounds.right) {
+            mCtrlType |= CTRL_RIGHT;
+            mCtrlType |= CTRL_TOP;
+        }
+        if (mTmpBottomRightCorner.contains(x, y)
+                && currentPipBounds.bottom != mDisplayBounds.bottom
+                && currentPipBounds.right != mDisplayBounds.right) {
+            mCtrlType |= CTRL_RIGHT;
+            mCtrlType |= CTRL_BOTTOM;
+        }
+        if (mTmpBottomLeftCorner.contains(x, y)
+                && currentPipBounds.bottom != mDisplayBounds.bottom
+                && currentPipBounds.left != mDisplayBounds.left) {
+            mCtrlType |= CTRL_LEFT;
+            mCtrlType |= CTRL_BOTTOM;
+        }
+    }
+
     private boolean isInValidSysUiState() {
         return mIsSysUiStateValid;
     }
@@ -364,6 +484,59 @@
         }
     }
 
+    private void onDragCornerResize(MotionEvent ev) {
+        int action = ev.getActionMasked();
+        float x = ev.getX();
+        float y = ev.getY() - mOhmOffset;
+        if (action == MotionEvent.ACTION_DOWN) {
+            mLastResizeBounds.setEmpty();
+            mAllowGesture = isInValidSysUiState() && isWithinDragResizeRegion((int) x, (int) y);
+            if (mAllowGesture) {
+                setCtrlType((int) x, (int) y);
+                mDownPoint.set(x, y);
+                mDownBounds.set(mPipBoundsState.getBounds());
+            }
+        } else if (mAllowGesture) {
+            switch (action) {
+                case MotionEvent.ACTION_POINTER_DOWN:
+                    // We do not support multi touch for resizing via drag
+                    mAllowGesture = false;
+                    break;
+                case MotionEvent.ACTION_MOVE:
+                    // Capture inputs
+                    if (!mThresholdCrossed
+                            && Math.hypot(x - mDownPoint.x, y - mDownPoint.y) > mTouchSlop) {
+                        mThresholdCrossed = true;
+                        // Reset the down to begin resizing from this point
+                        mDownPoint.set(x, y);
+                        mInputMonitor.pilferPointers();
+                    }
+                    if (mThresholdCrossed) {
+                        if (mPhonePipMenuController.isMenuVisible()) {
+                            mPhonePipMenuController.hideMenu(ANIM_TYPE_NONE,
+                                    false /* resize */);
+                        }
+                        final Rect currentPipBounds = mPipBoundsState.getBounds();
+                        mLastResizeBounds.set(TaskResizingAlgorithm.resizeDrag(x, y,
+                                mDownPoint.x, mDownPoint.y, currentPipBounds, mCtrlType, mMinSize.x,
+                                mMinSize.y, mMaxSize, true,
+                                mDownBounds.width() > mDownBounds.height()));
+                        mPipBoundsAlgorithm.transformBoundsToAspectRatio(mLastResizeBounds,
+                                mPipBoundsState.getAspectRatio(), false /* useCurrentMinEdgeSize */,
+                                true /* useCurrentSize */);
+                        mPipTaskOrganizer.scheduleUserResizePip(mDownBounds, mLastResizeBounds,
+                                null);
+                        mPipBoundsState.setHasUserResizedPip(true);
+                    }
+                    break;
+                case MotionEvent.ACTION_UP:
+                case MotionEvent.ACTION_CANCEL:
+                    finishResize();
+                    break;
+            }
+        }
+    }
+
     private void snapToMovementBoundsEdge(Rect bounds, Rect movementBounds) {
         final int leftEdge = bounds.left;
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
index 9c4e723..f4c2a33 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
@@ -213,7 +213,7 @@
         mPipResizeGestureHandler =
                 new PipResizeGestureHandler(context, pipBoundsAlgorithm, pipBoundsState,
                         mMotionHelper, mTouchState, pipTaskOrganizer, mPipDismissTargetHandler,
-                        this::updateMovementBounds, pipUiEventLogger,
+                        this::getMovementBounds, this::updateMovementBounds, pipUiEventLogger,
                         menuController, mainExecutor, mPipPerfHintController);
         mConnection = new PipAccessibilityInteractionConnection(mContext, pipBoundsState,
                 mMotionHelper, pipTaskOrganizer, mPipBoundsAlgorithm.getSnapAlgorithm(),
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
index 0ed5079..8ac7f89 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
@@ -476,6 +476,7 @@
         }
         mPipTaskOrganizer.removePip();
         mTvPipMenuController.closeMenu();
+        mPipNotificationController.dismiss();
     }
 
     @Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipAlphaAnimator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipAlphaAnimator.java
index 895c2ae..63c1512 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipAlphaAnimator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipAlphaAnimator.java
@@ -17,6 +17,7 @@
 package com.android.wm.shell.pip2.animation;
 
 import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
 import android.animation.ValueAnimator;
 import android.annotation.IntDef;
 import android.content.Context;
@@ -25,6 +26,7 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.wm.shell.R;
 import com.android.wm.shell.pip2.PipSurfaceTransactionHelper;
 
@@ -34,8 +36,7 @@
 /**
  * Animator that handles the alpha animation for entering PIP
  */
-public class PipAlphaAnimator extends ValueAnimator implements ValueAnimator.AnimatorUpdateListener,
-        ValueAnimator.AnimatorListener {
+public class PipAlphaAnimator extends ValueAnimator {
     @IntDef(prefix = {"FADE_"}, value = {
             FADE_IN,
             FADE_OUT
@@ -47,15 +48,45 @@
     public static final int FADE_IN = 0;
     public static final int FADE_OUT = 1;
 
-    private final int mEnterAnimationDuration;
     private final SurfaceControl mLeash;
     private final SurfaceControl.Transaction mStartTransaction;
 
+    private final Animator.AnimatorListener mAnimatorListener = new AnimatorListenerAdapter() {
+        @Override
+        public void onAnimationStart(Animator animation) {
+            super.onAnimationStart(animation);
+            if (mAnimationStartCallback != null) {
+                mAnimationStartCallback.run();
+            }
+            if (mStartTransaction != null) {
+                mStartTransaction.apply();
+            }
+        }
+
+        @Override
+        public void onAnimationEnd(Animator animation) {
+            super.onAnimationEnd(animation);
+            if (mAnimationEndCallback != null) {
+                mAnimationEndCallback.run();
+            }
+        }
+    };
+
+    private final ValueAnimator.AnimatorUpdateListener mAnimatorUpdateListener =
+            new AnimatorUpdateListener() {
+                @Override
+                public void onAnimationUpdate(@NonNull ValueAnimator animation) {
+                    final float alpha = (Float) animation.getAnimatedValue();
+                    mSurfaceControlTransactionFactory.getTransaction()
+                            .setAlpha(mLeash, alpha).apply();
+                }
+            };
+
     // optional callbacks for tracking animation start and end
     @Nullable private Runnable mAnimationStartCallback;
     @Nullable private Runnable mAnimationEndCallback;
 
-    private final PipSurfaceTransactionHelper.SurfaceControlTransactionFactory
+    @NonNull private PipSurfaceTransactionHelper.SurfaceControlTransactionFactory
             mSurfaceControlTransactionFactory;
 
     public PipAlphaAnimator(Context context,
@@ -71,11 +102,11 @@
         }
         mSurfaceControlTransactionFactory =
                 new PipSurfaceTransactionHelper.VsyncSurfaceControlTransactionFactory();
-        mEnterAnimationDuration = context.getResources()
+        final int enterAnimationDuration = context.getResources()
                 .getInteger(R.integer.config_pipEnterAnimationDuration);
-        setDuration(mEnterAnimationDuration);
-        addListener(this);
-        addUpdateListener(this);
+        setDuration(enterAnimationDuration);
+        addListener(mAnimatorListener);
+        addUpdateListener(mAnimatorUpdateListener);
     }
 
     public void setAnimationStartCallback(@NonNull Runnable runnable) {
@@ -86,32 +117,9 @@
         mAnimationEndCallback = runnable;
     }
 
-    @Override
-    public void onAnimationStart(@NonNull Animator animation) {
-        if (mAnimationStartCallback != null) {
-            mAnimationStartCallback.run();
-        }
-        if (mStartTransaction != null) {
-            mStartTransaction.apply();
-        }
+    @VisibleForTesting
+    void setSurfaceControlTransactionFactory(
+            @NonNull PipSurfaceTransactionHelper.SurfaceControlTransactionFactory factory) {
+        mSurfaceControlTransactionFactory = factory;
     }
-
-    @Override
-    public void onAnimationUpdate(@NonNull ValueAnimator animation) {
-        final float alpha = (Float) animation.getAnimatedValue();
-        mSurfaceControlTransactionFactory.getTransaction().setAlpha(mLeash, alpha).apply();
-    }
-
-    @Override
-    public void onAnimationEnd(@NonNull Animator animation) {
-        if (mAnimationEndCallback != null) {
-            mAnimationEndCallback.run();
-        }
-    }
-
-    @Override
-    public void onAnimationCancel(@NonNull Animator animation) {}
-
-    @Override
-    public void onAnimationRepeat(@NonNull Animator animation) {}
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipEnterAnimator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipEnterAnimator.java
index 5381a62..e513758 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipEnterAnimator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipEnterAnimator.java
@@ -23,6 +23,7 @@
 import android.animation.RectEvaluator;
 import android.animation.ValueAnimator;
 import android.content.Context;
+import android.content.pm.ActivityInfo;
 import android.graphics.Matrix;
 import android.graphics.PointF;
 import android.graphics.Rect;
@@ -33,10 +34,13 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
+import com.android.launcher3.icons.IconProvider;
 import com.android.wm.shell.R;
 import com.android.wm.shell.common.pip.PipUtils;
 import com.android.wm.shell.pip2.PipSurfaceTransactionHelper;
+import com.android.wm.shell.pip2.phone.PipAppIconOverlay;
 import com.android.wm.shell.shared.animation.Interpolators;
+import com.android.wm.shell.shared.pip.PipContentOverlay;
 
 /**
  * Animator that handles bounds animations for entering PIP.
@@ -59,6 +63,10 @@
 
     private final PipSurfaceTransactionHelper.SurfaceControlTransactionFactory
             mSurfaceControlTransactionFactory;
+    Matrix mTransformTensor = new Matrix();
+    final float[] mMatrixTmp = new float[9];
+    @Nullable private PipContentOverlay mContentOverlay;
+
 
     // Internal state representing initial transform - cached to avoid recalculation.
     private final PointF mInitScale = new PointF();
@@ -67,9 +75,6 @@
     private final PointF mInitActivityScale = new PointF();
     private final PointF mInitActivityPos = new PointF();
 
-    Matrix mTransformTensor = new Matrix();
-    final float[] mMatrixTmp = new float[9];
-
     public PipEnterAnimator(Context context,
             @NonNull SurfaceControl leash,
             SurfaceControl.Transaction startTransaction,
@@ -161,10 +166,15 @@
         mRectEvaluator.evaluate(fraction, initCrop, endCrop);
         tx.setCrop(mLeash, mAnimatedRect);
 
+        mTransformTensor.reset();
         mTransformTensor.setScale(scaleX, scaleY);
         mTransformTensor.postTranslate(posX, posY);
         mTransformTensor.postRotate(degrees);
         tx.setMatrix(mLeash, mTransformTensor, mMatrixTmp);
+
+        if (mContentOverlay != null) {
+            mContentOverlay.onAnimationUpdate(tx, 1f / scaleX, fraction, mEndBounds);
+        }
     }
 
     // no-ops
@@ -182,22 +192,51 @@
      * calculated differently from generic transitions.
      * @param pipChange PiP change received as a transition target.
      */
-    public void setEnterStartState(@NonNull TransitionInfo.Change pipChange,
-            @NonNull TransitionInfo.Change pipActivityChange) {
-        PipUtils.calcEndTransform(pipActivityChange, pipChange, mInitActivityScale,
-                mInitActivityPos);
-        if (mStartTransaction != null && pipActivityChange.getLeash() != null) {
-            mStartTransaction.setCrop(pipActivityChange.getLeash(), null);
-            mStartTransaction.setScale(pipActivityChange.getLeash(), mInitActivityScale.x,
-                    mInitActivityScale.y);
-            mStartTransaction.setPosition(pipActivityChange.getLeash(), mInitActivityPos.x,
-                    mInitActivityPos.y);
-            mFinishTransaction.setCrop(pipActivityChange.getLeash(), null);
-            mFinishTransaction.setScale(pipActivityChange.getLeash(), mInitActivityScale.x,
-                    mInitActivityScale.y);
-            mFinishTransaction.setPosition(pipActivityChange.getLeash(), mInitActivityPos.x,
-                    mInitActivityPos.y);
-        }
+    public void setEnterStartState(@NonNull TransitionInfo.Change pipChange) {
         PipUtils.calcStartTransform(pipChange, mInitScale, mInitPos, mInitCrop);
     }
+
+    /**
+     * Initializes and attaches an app icon overlay on top of the PiP layer.
+     */
+    public void setAppIconContentOverlay(Context context, Rect appBounds, Rect destinationBounds,
+            ActivityInfo activityInfo, int appIconSizePx) {
+        reattachAppIconOverlay(
+                new PipAppIconOverlay(context, appBounds, destinationBounds,
+                        new IconProvider(context).getIcon(activityInfo), appIconSizePx));
+    }
+
+    private void reattachAppIconOverlay(PipAppIconOverlay overlay) {
+        final SurfaceControl.Transaction tx =
+                mSurfaceControlTransactionFactory.getTransaction();
+        if (mContentOverlay != null) {
+            mContentOverlay.detach(tx);
+        }
+        mContentOverlay = overlay;
+        mContentOverlay.attach(tx, mLeash);
+    }
+
+    /**
+     * Clears the {@link #mContentOverlay}, this should be done after the content overlay is
+     * faded out.
+     */
+    public void clearAppIconOverlay() {
+        if (mContentOverlay == null) {
+            return;
+        }
+        SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction();
+        mContentOverlay.detach(tx);
+        mContentOverlay = null;
+    }
+
+    /**
+     * @return the app icon overlay leash; null if no overlay is attached.
+     */
+    @Nullable
+    public SurfaceControl getContentOverlayLeash() {
+        if (mContentOverlay == null) {
+            return null;
+        }
+        return mContentOverlay.getLeash();
+    }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PhonePipMenuController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PhonePipMenuController.java
index 8c1e5e6..58d2a85 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PhonePipMenuController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PhonePipMenuController.java
@@ -243,7 +243,6 @@
         mSystemWindows.updateViewLayout(mPipMenuView,
                 getPipMenuLayoutParams(mContext, MENU_WINDOW_TITLE, destinationBounds.width(),
                         destinationBounds.height()));
-        updateMenuLayout(destinationBounds);
     }
 
     /**
@@ -569,23 +568,6 @@
         }
     }
 
-    /**
-     * Tell the PIP Menu to recalculate its layout given its current position on the display.
-     */
-    public void updateMenuLayout(Rect bounds) {
-        final boolean isMenuVisible = isMenuVisible();
-        if (DEBUG) {
-            ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
-                    "%s: updateMenuLayout() state=%s"
-                            + " isMenuVisible=%s"
-                            + " callers=\n%s", TAG, mMenuState, isMenuVisible,
-                    Debug.getCallers(5, "    "));
-        }
-        if (isMenuVisible) {
-            mPipMenuView.updateMenuLayout(bounds);
-        }
-    }
-
     @Override
     public void onPipTransitionStateChanged(@PipTransitionState.TransitionState int oldState,
             @PipTransitionState.TransitionState int newState, Bundle extra) {
@@ -597,7 +579,6 @@
                 detach();
                 break;
             case PipTransitionState.CHANGED_PIP_BOUNDS:
-                updateMenuLayout(mPipBoundsState.getBounds());
                 hideMenu();
                 break;
             case PipTransitionState.CHANGING_PIP_BOUNDS:
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipAppIconOverlay.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipAppIconOverlay.java
new file mode 100644
index 0000000..b4cf890
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipAppIconOverlay.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.pip2.phone;
+
+import static android.util.TypedValue.COMPLEX_UNIT_DIP;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Matrix;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.util.TypedValue;
+import android.view.SurfaceControl;
+
+import com.android.wm.shell.shared.pip.PipContentOverlay;
+
+/** A {@link PipContentOverlay} shows app icon on solid color background. */
+public final class PipAppIconOverlay extends PipContentOverlay {
+    private static final String TAG = PipAppIconOverlay.class.getSimpleName();
+    // The maximum size for app icon in pixel.
+    private static final int MAX_APP_ICON_SIZE_DP = 72;
+
+    private final Context mContext;
+    private final int mAppIconSizePx;
+    private final Rect mAppBounds;
+    private final int mOverlayHalfSize;
+    private final Matrix mTmpTransform = new Matrix();
+    private final float[] mTmpFloat9 = new float[9];
+
+    private Bitmap mBitmap;
+
+    public PipAppIconOverlay(Context context, Rect appBounds, Rect destinationBounds,
+            Drawable appIcon, int appIconSizePx) {
+        mContext = context;
+        final int maxAppIconSizePx = (int) TypedValue.applyDimension(COMPLEX_UNIT_DIP,
+                MAX_APP_ICON_SIZE_DP, context.getResources().getDisplayMetrics());
+        mAppIconSizePx = Math.min(maxAppIconSizePx, appIconSizePx);
+
+        final int overlaySize = getOverlaySize(appBounds, destinationBounds);
+        mOverlayHalfSize = overlaySize >> 1;
+
+        // When the activity is in the secondary split, make sure the scaling center is not
+        // offset.
+        mAppBounds = new Rect(0, 0, appBounds.width(), appBounds.height());
+
+        mBitmap = Bitmap.createBitmap(overlaySize, overlaySize, Bitmap.Config.ARGB_8888);
+        prepareAppIconOverlay(appIcon);
+        mLeash = new SurfaceControl.Builder()
+                .setCallsite(TAG)
+                .setName(LAYER_NAME)
+                .build();
+    }
+
+    /**
+     * Returns the size of the app icon overlay.
+     *
+     * In order to have the overlay always cover the pip window during the transition,
+     * the overlay will be drawn with the max size of the start and end bounds in different
+     * rotation.
+     */
+    public static int getOverlaySize(Rect appBounds, Rect destinationBounds) {
+        final int appWidth = appBounds.width();
+        final int appHeight = appBounds.height();
+
+        return Math.max(Math.max(appWidth, appHeight),
+                Math.max(destinationBounds.width(), destinationBounds.height())) + 1;
+    }
+
+    @Override
+    public void attach(SurfaceControl.Transaction tx, SurfaceControl parentLeash) {
+        tx.show(mLeash);
+        tx.setLayer(mLeash, Integer.MAX_VALUE);
+        tx.setBuffer(mLeash, mBitmap.getHardwareBuffer());
+        tx.setAlpha(mLeash, 0f);
+        tx.reparent(mLeash, parentLeash);
+        tx.apply();
+    }
+
+    @Override
+    public void onAnimationUpdate(SurfaceControl.Transaction atomicTx,
+            float scale, float fraction, Rect endBounds) {
+        mTmpTransform.reset();
+        // Scale back the bitmap with the pivot at parent origin
+        mTmpTransform.setScale(scale, scale);
+        // We are negative-cropping away from the final bounds crop in config-at-end enter PiP;
+        // this means that the overlay shift depends on the final bounds.
+        // Note: translation is also dependent on the scaling of the parent.
+        mTmpTransform.postTranslate(endBounds.width() / 2f - mOverlayHalfSize * scale,
+                endBounds.height() / 2f - mOverlayHalfSize * scale);
+        atomicTx.setMatrix(mLeash, mTmpTransform, mTmpFloat9)
+                .setAlpha(mLeash, fraction < 0.5f ? 0 : (fraction - 0.5f) * 2);
+    }
+
+
+
+    @Override
+    public void detach(SurfaceControl.Transaction tx) {
+        super.detach(tx);
+        if (mBitmap != null && !mBitmap.isRecycled()) {
+            mBitmap.recycle();
+        }
+    }
+
+    private void prepareAppIconOverlay(Drawable appIcon) {
+        final Canvas canvas = new Canvas();
+        canvas.setBitmap(mBitmap);
+        final TypedArray ta = mContext.obtainStyledAttributes(new int[] {
+                android.R.attr.colorBackground });
+        try {
+            int colorAccent = ta.getColor(0, 0);
+            canvas.drawRGB(
+                    Color.red(colorAccent),
+                    Color.green(colorAccent),
+                    Color.blue(colorAccent));
+        } finally {
+            ta.recycle();
+        }
+        final Rect appIconBounds = new Rect(
+                mOverlayHalfSize - mAppIconSizePx / 2,
+                mOverlayHalfSize - mAppIconSizePx / 2,
+                mOverlayHalfSize + mAppIconSizePx / 2,
+                mOverlayHalfSize + mAppIconSizePx / 2);
+        appIcon.setBounds(appIconBounds);
+        appIcon.draw(canvas);
+        mBitmap = mBitmap.copy(Bitmap.Config.HARDWARE, false /* mutable */);
+    }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java
index 0427294..9a93371 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java
@@ -456,6 +456,10 @@
         }
     }
 
+    private void setLauncherAppIconSize(int iconSizePx) {
+        mPipBoundsState.getLauncherState().setAppIconSizePx(iconSizePx);
+    }
+
     /**
      * The interface for calls from outside the Shell, within the host process.
      */
@@ -571,7 +575,10 @@
         }
 
         @Override
-        public void setLauncherAppIconSize(int iconSizePx) {}
+        public void setLauncherAppIconSize(int iconSizePx) {
+            executeRemoteCallWithTaskPermission(mController, "setLauncherAppIconSize",
+                    (controller) -> controller.setLauncherAppIconSize(iconSizePx));
+        }
 
         @Override
         public void setPipAnimationListener(IPipAnimationListener listener) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipMenuIconsAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipMenuIconsAlgorithm.java
deleted file mode 100644
index ecb6ad6..0000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipMenuIconsAlgorithm.java
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.wm.shell.pip2.phone;
-
-import android.content.Context;
-import android.graphics.Rect;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.FrameLayout;
-
-/**
- * Helper class to calculate and place the menu icons on the PIP Menu.
- */
-public class PipMenuIconsAlgorithm {
-
-    private static final String TAG = "PipMenuIconsAlgorithm";
-
-    protected ViewGroup mViewRoot;
-    protected ViewGroup mTopEndContainer;
-    protected View mDragHandle;
-    protected View mSettingsButton;
-    protected View mDismissButton;
-
-    protected PipMenuIconsAlgorithm(Context context) {
-    }
-
-    /**
-     * Bind the necessary views.
-     */
-    public void bindViews(ViewGroup viewRoot, ViewGroup topEndContainer, View dragHandle,
-            View settingsButton, View dismissButton) {
-        mViewRoot = viewRoot;
-        mTopEndContainer = topEndContainer;
-        mDragHandle = dragHandle;
-        mSettingsButton = settingsButton;
-        mDismissButton = dismissButton;
-    }
-
-    /**
-     * Updates the position of the drag handle based on where the PIP window is on the screen.
-     */
-    public void onBoundsChanged(Rect bounds) {
-        // On phones, the menu icons are always static and will never move based on the PIP window
-        // position. No need to do anything here.
-    }
-
-    /**
-     * Set the gravity on the given view.
-     */
-    protected static void setLayoutGravity(View v, int gravity) {
-        if (v.getLayoutParams() instanceof FrameLayout.LayoutParams) {
-            FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) v.getLayoutParams();
-            params.gravity = gravity;
-            v.setLayoutParams(params);
-        }
-    }
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipMenuView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipMenuView.java
index 0910919..7cfaade 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipMenuView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipMenuView.java
@@ -145,7 +145,6 @@
     protected View mSettingsButton;
     protected View mDismissButton;
     protected View mTopEndContainer;
-    protected PipMenuIconsAlgorithm mPipMenuIconsAlgorithm;
 
     // How long the shell will wait for the app to close the PiP if a custom action is set.
     private final int mPipForceCloseDelay;
@@ -193,9 +192,6 @@
         mActionsGroup = findViewById(R.id.actions_group);
         mBetweenActionPaddingLand = getResources().getDimensionPixelSize(
                 R.dimen.pip_between_action_padding_land);
-        mPipMenuIconsAlgorithm = new PipMenuIconsAlgorithm(mContext);
-        mPipMenuIconsAlgorithm.bindViews((ViewGroup) mViewRoot, (ViewGroup) mTopEndContainer,
-                findViewById(R.id.resize_handle), mSettingsButton, mDismissButton);
         mDismissFadeOutDurationMs = context.getResources()
                 .getInteger(R.integer.config_pipExitAnimationDuration);
 
@@ -339,10 +335,6 @@
         cancelDelayedHide();
     }
 
-    void updateMenuLayout(Rect bounds) {
-        mPipMenuIconsAlgorithm.onBoundsChanged(bounds);
-    }
-
     void hideMenu() {
         hideMenu(null);
     }
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 810eff8..17392bc 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
@@ -752,11 +752,11 @@
                                 PipTransition.BOUNDS_CHANGE_JUMPCUT_DURATION));
                 break;
             case PipTransitionState.CHANGING_PIP_BOUNDS:
-                SurfaceControl.Transaction startTx = extra.getParcelable(
+                final SurfaceControl.Transaction startTx = extra.getParcelable(
                         PipTransition.PIP_START_TX, SurfaceControl.Transaction.class);
-                SurfaceControl.Transaction finishTx = extra.getParcelable(
+                final SurfaceControl.Transaction finishTx = extra.getParcelable(
                         PipTransition.PIP_FINISH_TX, SurfaceControl.Transaction.class);
-                Rect destinationBounds = extra.getParcelable(
+                final Rect destinationBounds = extra.getParcelable(
                         PipTransition.PIP_DESTINATION_BOUNDS, Rect.class);
                 final int duration = extra.getInt(ANIMATING_BOUNDS_CHANGE_DURATION,
                         PipTransition.BOUNDS_CHANGE_JUMPCUT_DURATION);
@@ -794,7 +794,7 @@
         cleanUpHighPerfSessionMaybe();
 
         // Signal that the transition is done - should update transition state by default.
-        mPipScheduler.scheduleFinishResizePip(destinationBounds, false /* configAtEnd */);
+        mPipScheduler.scheduleFinishResizePip(destinationBounds);
     }
 
     private void startResizeAnimation(SurfaceControl.Transaction startTx,
@@ -803,11 +803,8 @@
         Preconditions.checkState(pipLeash != null,
                 "No leash cached by mPipTransitionState=" + mPipTransitionState);
 
-        startTx.setWindowCrop(pipLeash, mPipBoundsState.getBounds().width(),
-                mPipBoundsState.getBounds().height());
-
         PipResizeAnimator animator = new PipResizeAnimator(mContext, pipLeash,
-                startTx, finishTx, mPipBoundsState.getBounds(), mPipBoundsState.getBounds(),
+                startTx, finishTx, destinationBounds, mPipBoundsState.getBounds(),
                 destinationBounds, duration, 0f /* angle */);
         animator.setAnimationEndCallback(() -> {
             // In case an ongoing drag/fling was present before a deterministic resize transition
@@ -818,7 +815,7 @@
 
             cleanUpHighPerfSessionMaybe();
             // Signal that we are done with resize transition
-            mPipScheduler.scheduleFinishResizePip(destinationBounds, true /* configAtEnd */);
+            mPipScheduler.scheduleFinishResizePip(destinationBounds);
         });
         animator.start();
     }
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 f5ef64d..751175f 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
@@ -535,28 +535,26 @@
                 Preconditions.checkState(pipLeash != null,
                         "No leash cached by mPipTransitionState=" + mPipTransitionState);
 
-                SurfaceControl.Transaction startTx = extra.getParcelable(
+                final SurfaceControl.Transaction startTx = extra.getParcelable(
                         PipTransition.PIP_START_TX, SurfaceControl.Transaction.class);
-                SurfaceControl.Transaction finishTx = extra.getParcelable(
+                final SurfaceControl.Transaction finishTx = extra.getParcelable(
                         PipTransition.PIP_FINISH_TX, SurfaceControl.Transaction.class);
+                final Rect destinationBounds = extra.getParcelable(
+                        PipTransition.PIP_DESTINATION_BOUNDS, Rect.class);
                 final int duration = extra.getInt(ANIMATING_BOUNDS_CHANGE_DURATION,
                         PipTransition.BOUNDS_CHANGE_JUMPCUT_DURATION);
 
-                startTx.setWindowCrop(pipLeash, mPipBoundsState.getBounds().width(),
-                        mPipBoundsState.getBounds().height());
-
                 PipResizeAnimator animator = new PipResizeAnimator(mContext, pipLeash,
-                        startTx, finishTx, mPipBoundsState.getBounds(), mStartBoundsAfterRelease,
-                        mLastResizeBounds, duration, mAngle);
+                        startTx, finishTx, destinationBounds, mStartBoundsAfterRelease,
+                        destinationBounds, duration, mAngle);
                 animator.setAnimationEndCallback(() -> {
                     // All motion operations have actually finished, so make bounds cache updates.
-                    mUserResizeBounds.set(mLastResizeBounds);
+                    mUserResizeBounds.set(destinationBounds);
                     resetState();
                     cleanUpHighPerfSessionMaybe();
 
                     // Signal that we are done with resize transition
-                    mPipScheduler.scheduleFinishResizePip(
-                            mLastResizeBounds, true /* configAtEnd */);
+                    mPipScheduler.scheduleFinishResizePip(destinationBounds);
                 });
                 animator.start();
                 break;
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 fbbf6f3..8b25b11 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
@@ -175,22 +175,12 @@
      * Note that we do not allow any actual WM Core changes at this point.
      *
      * @param toBounds destination bounds used only for internal state updates - not sent to Core.
-     * @param configAtEnd true if we are waiting for config updates at the end of the transition.
      */
-    public void scheduleFinishResizePip(Rect toBounds, boolean configAtEnd) {
-        // Make updates to the internal state to reflect new bounds
+    public void scheduleFinishResizePip(Rect toBounds) {
+        // Make updates to the internal state to reflect new bounds before updating any transitions
+        // related state; transition state updates can trigger callbacks that use the cached bounds.
         onFinishingPipResize(toBounds);
-
-        SurfaceControl.Transaction tx = null;
-        if (configAtEnd) {
-            tx = new SurfaceControl.Transaction();
-            tx.addTransactionCommittedListener(mMainExecutor, () -> {
-                mPipTransitionState.setState(PipTransitionState.CHANGED_PIP_BOUNDS);
-            });
-        } else {
-            mPipTransitionState.setState(PipTransitionState.CHANGED_PIP_BOUNDS);
-        }
-        mPipTransitionController.finishTransition(tx);
+        mPipTransitionController.finishTransition();
     }
 
     /**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTaskListener.java
index 373ec80..2c7584a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTaskListener.java
@@ -177,8 +177,7 @@
                             mPipBoundsState.getBounds(), destinationBounds, duration,
                             0f /* delta */);
                     animator.setAnimationEndCallback(() -> {
-                        mPipScheduler.scheduleFinishResizePip(
-                                destinationBounds, false /* configAtEnd */);
+                        mPipScheduler.scheduleFinishResizePip(destinationBounds);
                     });
                     animator.start();
                 }
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 b286211..64d8887 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
@@ -30,14 +30,12 @@
 import static com.android.wm.shell.transition.Transitions.TRANSIT_REMOVE_PIP;
 import static com.android.wm.shell.transition.Transitions.TRANSIT_RESIZE_PIP;
 
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ValueAnimator;
 import android.annotation.NonNull;
 import android.app.ActivityManager;
 import android.app.PictureInPictureParams;
 import android.content.Context;
 import android.graphics.Point;
+import android.graphics.PointF;
 import android.graphics.Rect;
 import android.os.Bundle;
 import android.os.IBinder;
@@ -279,21 +277,25 @@
         if (pipChange == null) {
             return false;
         }
-        SurfaceControl pipLeash = pipChange.getLeash();
+        // We expect the PiP activity as a separate change in a config-at-end transition;
+        // only flings are not using config-at-end for resize bounds changes
+        TransitionInfo.Change pipActivityChange = getDeferConfigActivityChange(info,
+                pipChange.getTaskInfo().getToken());
+        if (pipActivityChange != null) {
+            // Transform calculations use PiP params by default, so make sure they are null to
+            // default to using bounds for scaling calculations instead.
+            pipChange.getTaskInfo().pictureInPictureParams = null;
+            prepareConfigAtEndActivity(startTransaction, finishTransaction, pipChange,
+                    pipActivityChange);
+        }
 
-        // Even though the final bounds and crop are applied with finishTransaction since
-        // this is a visible change, we still need to handle the app draw coming in. Snapshot
-        // covering app draw during collection will be removed by startTransaction. So we make
-        // the crop equal to the final bounds and then let the current
-        // animator scale the leash back to starting bounds.
-        // Note: animator is responsible for applying the startTx but NOT finishTx.
+        SurfaceControl pipLeash = pipChange.getLeash();
         startTransaction.setWindowCrop(pipLeash, pipChange.getEndAbsBounds().width(),
                 pipChange.getEndAbsBounds().height());
 
-        // TODO: b/275910498 Couple this routine with a new implementation of the PiP animator.
         // Classes interested in continuing the animation would subscribe to this state update
-        // getting info such as endBounds, startTx, and finishTx as an extra Bundle once
-        // animators are in place. Once done state needs to be updated to CHANGED_PIP_BOUNDS.
+        // getting info such as endBounds, startTx, and finishTx as an extra Bundle
+        // Once done state needs to be updated to CHANGED_PIP_BOUNDS via {@link PipScheduler#}.
         Bundle extra = new Bundle();
         extra.putParcelable(PIP_START_TX, startTransaction);
         extra.putParcelable(PIP_FINISH_TX, finishTransaction);
@@ -356,37 +358,25 @@
             sourceRectHint = pipChange.getTaskInfo().pictureInPictureParams.getSourceRectHint();
         }
 
+        prepareConfigAtEndActivity(startTransaction, finishTransaction, pipChange,
+                pipActivityChange);
         startTransaction.merge(finishTransaction);
         PipEnterAnimator animator = new PipEnterAnimator(mContext, pipLeash,
                 startTransaction, finishTransaction, destinationBounds, sourceRectHint, delta);
-        animator.setEnterStartState(pipChange, pipActivityChange);
+        animator.setEnterStartState(pipChange);
         animator.onEnterAnimationUpdate(1.0f /* fraction */, startTransaction);
         startTransaction.apply();
-        finishInner();
-        return true;
-    }
 
-    private void startOverlayFadeoutAnimation() {
-        ValueAnimator animator = ValueAnimator.ofFloat(1f, 0f);
-        animator.setDuration(CONTENT_OVERLAY_FADE_OUT_DELAY_MS);
-        animator.addListener(new AnimatorListenerAdapter() {
-            @Override
-            public void onAnimationEnd(Animator animation) {
-                super.onAnimationEnd(animation);
+        if (swipePipToHomeOverlay != null) {
+            // fadeout the overlay if needed.
+            startOverlayFadeoutAnimation(swipePipToHomeOverlay, () -> {
                 SurfaceControl.Transaction tx = new SurfaceControl.Transaction();
-                tx.remove(mPipTransitionState.getSwipePipToHomeOverlay());
+                tx.remove(swipePipToHomeOverlay);
                 tx.apply();
-
-                // We have fully completed enter-PiP animation after the overlay is gone.
-                mPipTransitionState.setState(PipTransitionState.ENTERED_PIP);
-            }
-        });
-        animator.addUpdateListener(animation -> {
-            float alpha = (float) animation.getAnimatedValue();
-            SurfaceControl.Transaction tx = new SurfaceControl.Transaction();
-            tx.setAlpha(mPipTransitionState.getSwipePipToHomeOverlay(), alpha).apply();
-        });
-        animator.start();
+            });
+        }
+        finishTransition();
+        return true;
     }
 
     private boolean startBoundsTypeEnterAnimation(@NonNull TransitionInfo info,
@@ -405,15 +395,17 @@
             return false;
         }
 
-        Rect endBounds = pipChange.getEndAbsBounds();
-        SurfaceControl pipLeash = mPipTransitionState.mPinnedTaskLeash;
-        Preconditions.checkNotNull(pipLeash, "Leash is null for bounds transition.");
+        final Rect startBounds = pipChange.getStartAbsBounds();
+        final Rect endBounds = pipChange.getEndAbsBounds();
 
-        Rect sourceRectHint = null;
-        if (pipChange.getTaskInfo() != null
-                && pipChange.getTaskInfo().pictureInPictureParams != null) {
-            sourceRectHint = pipChange.getTaskInfo().pictureInPictureParams.getSourceRectHint();
-        }
+        final PictureInPictureParams params = pipChange.getTaskInfo().pictureInPictureParams;
+        final float aspectRatio = mPipBoundsAlgorithm.getAspectRatioOrDefault(params);
+        final Rect sourceRectHint = PipBoundsAlgorithm.getValidSourceHintRect(params, startBounds,
+                endBounds);
+        final Rect adjustedSourceRectHint = sourceRectHint != null ? new Rect(sourceRectHint)
+                : PipUtils.getEnterPipWithOverlaySrcRectHint(startBounds, aspectRatio);
+
+        final SurfaceControl pipLeash = mPipTransitionState.mPinnedTaskLeash;
 
         // For opening type transitions, if there is a change of mode TO_FRONT/OPEN,
         // make sure that change has alpha of 1f, since it's init state might be set to alpha=0f
@@ -441,14 +433,38 @@
         }
 
         PipEnterAnimator animator = new PipEnterAnimator(mContext, pipLeash,
-                startTransaction, finishTransaction, endBounds, sourceRectHint, delta);
-        animator.setAnimationStartCallback(() -> animator.setEnterStartState(pipChange,
-                pipActivityChange));
-        animator.setAnimationEndCallback(this::finishInner);
+                startTransaction, finishTransaction, endBounds, adjustedSourceRectHint, delta);
+        if (sourceRectHint == null) {
+            // update the src-rect-hint in params in place, to set up initial animator transform.
+            params.getSourceRectHint().set(adjustedSourceRectHint);
+            animator.setAppIconContentOverlay(
+                    mContext, startBounds, endBounds, pipChange.getTaskInfo().topActivityInfo,
+                    mPipBoundsState.getLauncherState().getAppIconSizePx());
+        }
+
+        prepareConfigAtEndActivity(startTransaction, finishTransaction, pipChange,
+                pipActivityChange);
+        animator.setAnimationStartCallback(() -> animator.setEnterStartState(pipChange));
+        animator.setAnimationEndCallback(() -> {
+            if (animator.getContentOverlayLeash() != null) {
+                startOverlayFadeoutAnimation(animator.getContentOverlayLeash(),
+                        animator::clearAppIconOverlay);
+            }
+            finishTransition();
+        });
         animator.start();
         return true;
     }
 
+    private void startOverlayFadeoutAnimation(@NonNull SurfaceControl overlayLeash,
+            @NonNull Runnable onAnimationEnd) {
+        PipAlphaAnimator animator = new PipAlphaAnimator(mContext, overlayLeash,
+                null /* startTx */, PipAlphaAnimator.FADE_OUT);
+        animator.setDuration(CONTENT_OVERLAY_FADE_OUT_DELAY_MS);
+        animator.setAnimationEndCallback(onAnimationEnd);
+        animator.start();
+    }
+
     private void handleBoundsTypeFixedRotation(TransitionInfo.Change pipTaskChange,
             TransitionInfo.Change pipActivityChange, int endRotation) {
         final Rect endBounds = pipTaskChange.getEndAbsBounds();
@@ -504,8 +520,7 @@
         PipAlphaAnimator animator = new PipAlphaAnimator(mContext, pipLeash, startTransaction,
                 PipAlphaAnimator.FADE_IN);
         // This should update the pip transition state accordingly after we stop playing.
-        animator.setAnimationEndCallback(this::finishInner);
-
+        animator.setAnimationEndCallback(this::finishTransition);
         animator.start();
         return true;
     }
@@ -561,12 +576,7 @@
         PipExpandAnimator animator = new PipExpandAnimator(mContext, pipLeash,
                 startTransaction, finishTransaction, endBounds, startBounds, endBounds,
                 sourceRectHint, Surface.ROTATION_0);
-
-        animator.setAnimationEndCallback(() -> {
-            mPipTransitionState.setState(PipTransitionState.EXITED_PIP);
-            finishCallback.onTransitionFinished(null);
-        });
-
+        animator.setAnimationEndCallback(this::finishTransition);
         animator.start();
         return true;
     }
@@ -690,35 +700,54 @@
         return isPipMovedToBack || isPipClosed || isPipDismissed;
     }
 
+    private void prepareConfigAtEndActivity(@NonNull SurfaceControl.Transaction startTx,
+            @NonNull SurfaceControl.Transaction finishTx,
+            @NonNull TransitionInfo.Change pipChange,
+            @NonNull TransitionInfo.Change pipActivityChange) {
+        PointF initActivityScale = new PointF();
+        PointF initActivityPos = new PointF();
+        PipUtils.calcEndTransform(pipActivityChange, pipChange, initActivityScale,
+                initActivityPos);
+        if (pipActivityChange.getLeash() != null) {
+            startTx.setCrop(pipActivityChange.getLeash(), null);
+            startTx.setScale(pipActivityChange.getLeash(), initActivityScale.x,
+                    initActivityScale.y);
+            startTx.setPosition(pipActivityChange.getLeash(), initActivityPos.x,
+                    initActivityPos.y);
+
+            finishTx.setCrop(pipActivityChange.getLeash(), null);
+            finishTx.setScale(pipActivityChange.getLeash(), initActivityScale.x,
+                    initActivityScale.y);
+            finishTx.setPosition(pipActivityChange.getLeash(), initActivityPos.x,
+                    initActivityPos.y);
+        }
+    }
+
     //
     // Miscellaneous callbacks and listeners
     //
 
-    private void finishInner() {
-        finishTransition(null /* tx */);
-        if (mPipTransitionState.getSwipePipToHomeOverlay() != null) {
-            startOverlayFadeoutAnimation();
-        } else if (mPipTransitionState.getState() == PipTransitionState.ENTERING_PIP) {
-            // If we were entering PiP (i.e. playing the animation) with a valid srcRectHint,
-            // and then we get a signal on client finishing its draw after the transition
-            // has ended, then we have fully entered PiP.
-            mPipTransitionState.setState(PipTransitionState.ENTERED_PIP);
-        }
-    }
-
     @Override
-    public void finishTransition(@Nullable SurfaceControl.Transaction tx) {
-        WindowContainerTransaction wct = null;
-        if (tx != null && mPipTransitionState.mPipTaskToken != null) {
-            // Outside callers can only provide a transaction to be applied with the final draw.
-            // So no actual WM changes can be applied for this transition after this point.
-            wct = new WindowContainerTransaction();
-            wct.setBoundsChangeTransaction(mPipTransitionState.mPipTaskToken, tx);
-        }
+    public void finishTransition() {
         if (mFinishCallback != null) {
-            mFinishCallback.onTransitionFinished(wct);
+            mFinishCallback.onTransitionFinished(null /* finishWct */);
             mFinishCallback = null;
         }
+
+        final int currentState = mPipTransitionState.getState();
+        int nextState = PipTransitionState.UNDEFINED;
+        switch (currentState) {
+            case PipTransitionState.ENTERING_PIP:
+                nextState = PipTransitionState.ENTERED_PIP;
+                break;
+            case PipTransitionState.CHANGING_PIP_BOUNDS:
+                nextState = PipTransitionState.CHANGED_PIP_BOUNDS;
+                break;
+            case PipTransitionState.EXITING_PIP:
+                nextState = PipTransitionState.EXITED_PIP;
+                break;
+        }
+        mPipTransitionState.setState(nextState);
     }
 
     @Override
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 7893267..839bb4e 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
@@ -1625,6 +1625,21 @@
                 leftTopTaskId = mainStageTopTaskId;
                 rightBottomTaskId = sideStageTopTaskId;
             }
+
+            if (Flags.enableFlexibleTwoAppSplit()) {
+                // Split screen can be laid out in such a way that some of the apps are offscreen.
+                // For the purposes of passing SplitBounds up to launcher (for use in thumbnails
+                // etc.), we crop the bounds down to the screen size.
+                topLeftBounds.left =
+                        Math.max(topLeftBounds.left, 0);
+                topLeftBounds.top =
+                        Math.max(topLeftBounds.top, 0);
+                bottomRightBounds.right =
+                        Math.min(bottomRightBounds.right, mSplitLayout.getDisplayWidth());
+                bottomRightBounds.top =
+                        Math.min(bottomRightBounds.top, mSplitLayout.getDisplayHeight());
+            }
+
             SplitBounds splitBounds = new SplitBounds(topLeftBounds, bottomRightBounds,
                     leftTopTaskId, rightBottomTaskId, mSplitLayout.calculateCurrentSnapPosition());
             if (mainStageTopTaskId != INVALID_TASK_ID && sideStageTopTaskId != INVALID_TASK_ID) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/OWNERS b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/OWNERS
new file mode 100644
index 0000000..892d150
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/OWNERS
@@ -0,0 +1,5 @@
+# Bubbles team
+madym@google.com
+atsjenk@google.com
+liranb@google.com
+mpodolian@google.com
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java
index e74342e..eaeedba 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java
@@ -40,6 +40,7 @@
 import android.window.WindowContainerToken;
 import android.window.WindowContainerTransaction;
 
+import com.android.wm.shell.Flags;
 import com.android.wm.shell.ShellTaskOrganizer;
 import com.android.wm.shell.common.SyncTransactionQueue;
 
@@ -525,8 +526,13 @@
      */
     void removeTask() {
         if (mTaskToken == null) {
-            // Call to remove task before we have one, do nothing
-            Slog.w(TAG, "Trying to remove a task that was never added? (no taskToken)");
+            if (Flags.enableTaskViewControllerCleanup()) {
+                // We don't have a task yet. Only clean up the controller
+                mTaskViewTransitions.removeTaskView(this);
+            } else {
+                // Call to remove task before we have one, do nothing
+                Slog.w(TAG, "Trying to remove a task that was never added? (no taskToken)");
+            }
             return;
         }
         mShellExecutor.execute(() -> {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java
index 39648f6..ae75cb5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java
@@ -37,11 +37,14 @@
 
 import androidx.annotation.VisibleForTesting;
 
+import com.android.wm.shell.Flags;
 import com.android.wm.shell.shared.TransitionUtil;
 import com.android.wm.shell.transition.Transitions;
 
 import java.util.ArrayList;
+import java.util.Map;
 import java.util.Objects;
+import java.util.WeakHashMap;
 
 /**
  * Handles Shell Transitions that involve TaskView tasks.
@@ -49,8 +52,15 @@
 public class TaskViewTransitions implements Transitions.TransitionHandler {
     static final String TAG = "TaskViewTransitions";
 
-    private final ArrayMap<TaskViewTaskController, TaskViewRequestedState> mTaskViews =
-            new ArrayMap<>();
+    /**
+     * Map of {@link TaskViewTaskController} to {@link TaskViewRequestedState}.
+     * <p>
+     * {@link TaskView} keeps a reference to the {@link TaskViewTaskController} instance and
+     * manages its lifecycle.
+     * Only keep a weak reference to the controller instance here to allow for it to be cleaned
+     * up when its TaskView is no longer used.
+     */
+    private final Map<TaskViewTaskController, TaskViewRequestedState> mTaskViews;
     private final ArrayList<PendingTransition> mPending = new ArrayList<>();
     private final Transitions mTransitions;
     private final boolean[] mRegistered = new boolean[]{false};
@@ -95,6 +105,11 @@
 
     public TaskViewTransitions(Transitions transitions) {
         mTransitions = transitions;
+        if (Flags.enableTaskViewControllerCleanup()) {
+            mTaskViews = new WeakHashMap<>();
+        } else {
+            mTaskViews = new ArrayMap<>();
+        }
         // Defer registration until the first TaskView because we want this to be the "first" in
         // priority when handling requests.
         // TODO(210041388): register here once we have an explicit ordering mechanism.
@@ -208,10 +223,21 @@
     }
 
     private TaskViewTaskController findTaskView(ActivityManager.RunningTaskInfo taskInfo) {
-        for (int i = 0; i < mTaskViews.size(); ++i) {
-            if (mTaskViews.keyAt(i).getTaskInfo() == null) continue;
-            if (taskInfo.token.equals(mTaskViews.keyAt(i).getTaskInfo().token)) {
-                return mTaskViews.keyAt(i);
+        if (Flags.enableTaskViewControllerCleanup()) {
+            for (TaskViewTaskController controller : mTaskViews.keySet()) {
+                if (controller.getTaskInfo() == null) continue;
+                if (taskInfo.token.equals(controller.getTaskInfo().token)) {
+                    return controller;
+                }
+            }
+        } else {
+            ArrayMap<TaskViewTaskController, TaskViewRequestedState> taskViews =
+                    (ArrayMap<TaskViewTaskController, TaskViewRequestedState>) mTaskViews;
+            for (int i = 0; i < taskViews.size(); ++i) {
+                if (taskViews.keyAt(i).getTaskInfo() == null) continue;
+                if (taskInfo.token.equals(taskViews.keyAt(i).getTaskInfo().token)) {
+                    return taskViews.keyAt(i);
+                }
             }
         }
         return 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 0d89f75..17483dd 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
@@ -40,6 +40,7 @@
 import android.window.WindowContainerTransaction;
 
 import com.android.internal.protolog.ProtoLog;
+import com.android.window.flags.Flags;
 import com.android.wm.shell.ShellTaskOrganizer;
 import com.android.wm.shell.activityembedding.ActivityEmbeddingController;
 import com.android.wm.shell.common.split.SplitScreenUtils;
@@ -419,7 +420,9 @@
         for (int i = 0; i < info.getRootCount(); ++i) {
             out.addRoot(info.getRoot(i));
         }
-        out.setAnimationOptions(info.getAnimationOptions());
+        if (!Flags.moveAnimationOptionsToChange()) {
+            out.setAnimationOptions(info.getAnimationOptions());
+        }
         return out;
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
index 5437167..ec58292 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
@@ -561,13 +561,12 @@
 
                 final TransitionInfo.AnimationOptions options;
                 if (Flags.moveAnimationOptionsToChange()) {
-                    options = info.getAnimationOptions();
-                } else {
                     options = change.getAnimationOptions();
+                } else {
+                    options = info.getAnimationOptions();
                 }
                 if (options != null) {
-                    attachThumbnail(animations, onAnimFinish, change, info.getAnimationOptions(),
-                            cornerRadius);
+                    attachThumbnail(animations, onAnimFinish, change, options, cornerRadius);
                 }
             }
         }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
index fde01ee..3946b61 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
@@ -290,9 +290,11 @@
 
         final Resources res = mResult.mRootView.getResources();
         mDragResizeListener.setGeometry(new DragResizeWindowGeometry(0 /* taskCornerRadius */,
-                new Size(mResult.mWidth, mResult.mHeight), getResizeEdgeHandleSize(res),
-                getResizeHandleEdgeInset(res), getFineResizeCornerSize(res),
-                getLargeResizeCornerSize(res)), touchSlop);
+                        new Size(mResult.mWidth, mResult.mHeight),
+                        getResizeEdgeHandleSize(res),
+                        getResizeHandleEdgeInset(res), getFineResizeCornerSize(res),
+                        getLargeResizeCornerSize(res), DragResizeWindowGeometry.DisabledEdge.NONE),
+                touchSlop);
     }
 
     /**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopHandleManageWindowsMenu.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopHandleManageWindowsMenu.kt
index e71b4f3..7b71e41 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopHandleManageWindowsMenu.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopHandleManageWindowsMenu.kt
@@ -53,11 +53,8 @@
     private var menuViewContainer: AdditionalViewContainer? = null
 
     init {
-        show(snapshotList, onIconClickListener, onOutsideClickListener)
-    }
-
-    override fun close() {
-        menuViewContainer?.releaseView()
+        createMenu(snapshotList, onIconClickListener, onOutsideClickListener)
+        animateOpen()
     }
 
     private fun calculateMenuPosition(): Point {
@@ -106,4 +103,8 @@
             view = menuView.rootView,
         )
     }
+
+    override fun removeFromContainer() {
+        menuViewContainer?.releaseView()
+    }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopHeaderManageWindowsMenu.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopHeaderManageWindowsMenu.kt
index 173bc08..dd68105 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopHeaderManageWindowsMenu.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopHeaderManageWindowsMenu.kt
@@ -65,11 +65,10 @@
     var menuViewContainer: AdditionalViewContainer? = null
 
     init {
-        show(snapshotList, onIconClickListener, onOutsideClickListener)
-    }
-
-    override fun close() {
-        menuViewContainer?.releaseView()
+        createMenu(snapshotList, onIconClickListener, onOutsideClickListener)
+        menuView.rootView.pivotX = 0f
+        menuView.rootView.pivotY = 0f
+        animateOpen()
     }
 
     override fun addToContainer(menuView: ManageWindowsView) {
@@ -139,4 +138,8 @@
             surfaceControlTransactionSupplier
         )
     }
+
+    override fun removeFromContainer() {
+        menuViewContainer?.releaseView()
+    }
 }
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 a775cbc..a3324cc6 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
@@ -130,6 +130,7 @@
 import com.android.wm.shell.transition.FocusTransitionObserver;
 import com.android.wm.shell.transition.Transitions;
 import com.android.wm.shell.windowdecor.DesktopModeWindowDecoration.ExclusionRegionListener;
+import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalSystemViewContainer;
 import com.android.wm.shell.windowdecor.extension.InsetsStateKt;
 import com.android.wm.shell.windowdecor.extension.TaskInfoKt;
 import com.android.wm.shell.windowdecor.viewholder.AppHeaderViewHolder;
@@ -180,12 +181,13 @@
     private boolean mTransitionDragActive;
 
     private SparseArray<EventReceiver> mEventReceiversByDisplay = new SparseArray<>();
+    private DesktopStatusBarInputLayerSupplier mStatusBarInputLayerSupplier;
 
     private final ExclusionRegionListener mExclusionRegionListener =
             new ExclusionRegionListenerImpl();
 
     private final SparseArray<DesktopModeWindowDecoration> mWindowDecorByTaskId;
-    private final DragStartListenerImpl mDragStartListener = new DragStartListenerImpl();
+    private final DragEventListenerImpl mDragEventListener = new DragEventListenerImpl();
     private final InputMonitorFactory mInputMonitorFactory;
     private TaskOperations mTaskOperations;
     private final Supplier<SurfaceControl.Transaction> mTransactionFactory;
@@ -418,7 +420,11 @@
                         return Unit.INSTANCE;
                     });
         }
-        mFocusTransitionObserver.setLocalFocusTransitionListener(this, mMainExecutor);
+        if (Flags.enableHandleInputFix()) {
+            mStatusBarInputLayerSupplier =
+                    new DesktopStatusBarInputLayerSupplier(mContext, mMainHandler);
+            mFocusTransitionObserver.setLocalFocusTransitionListener(this, mMainExecutor);
+        }
     }
 
     @Override
@@ -474,6 +480,7 @@
             removeTaskFromEventReceiver(oldTaskInfo.displayId);
             incrementEventReceiverTasks(taskInfo.displayId);
         }
+        decoration.setStatusBarInputLayer(getStatusBarInputLayer(taskInfo));
         decoration.relayout(taskInfo, decoration.mHasGlobalFocus);
         mActivityOrientationChangeHandler.ifPresent(handler ->
                 handler.handleActivityOrientationChange(oldTaskInfo, taskInfo));
@@ -512,6 +519,7 @@
         if (decoration == null) {
             createWindowDecoration(taskInfo, taskSurface, startT, finishT);
         } else {
+            decoration.setStatusBarInputLayer(getStatusBarInputLayer(taskInfo));
             decoration.relayout(taskInfo, startT, finishT, false /* applyStartTransactionOnDraw */,
                     false /* shouldSetTaskPositionAndCrop */,
                     mFocusTransitionObserver.hasGlobalFocus(taskInfo));
@@ -578,6 +586,7 @@
             return;
         }
         mDesktopTasksController.toggleDesktopTaskFullImmersiveState(decoration.mTaskInfo);
+        decoration.closeMaximizeMenu();
     }
 
     private void onSnapResize(int taskId, boolean left, MotionEvent motionEvent) {
@@ -604,7 +613,8 @@
                     decoration.mTaskInfo.configuration.windowConfiguration.getBounds(),
                     left ? SnapPosition.LEFT : SnapPosition.RIGHT,
                     resizeTrigger,
-                    motionEvent);
+                    motionEvent,
+                    mWindowDecorByTaskId.get(taskId));
         }
 
         decoration.closeHandleMenu();
@@ -663,7 +673,7 @@
         decoration.closeHandleMenu();
         // When the app enters split-select, the handle will no longer be visible, meaning
         // we shouldn't receive input for it any longer.
-        decoration.disposeStatusBarInputLayer();
+        decoration.detachStatusBarInputLayer();
         mDesktopTasksController.requestSplit(decoration.mTaskInfo, false /* leftOrTop */);
     }
 
@@ -1063,7 +1073,8 @@
                             taskInfo, decoration.mTaskSurface, position,
                             new PointF(e.getRawX(dragPointerIdx), e.getRawY(dragPointerIdx)),
                             newTaskBounds, decoration.calculateValidDragArea(),
-                            new Rect(mOnDragStartInitialBounds), e);
+                            new Rect(mOnDragStartInitialBounds), e,
+                            mWindowDecorByTaskId.get(taskInfo.taskId));
                     if (touchingButton && !mHasLongClicked) {
                         // We need the input event to not be consumed here to end the ripple
                         // effect on the touched button. We will reset drag state in the ensuing
@@ -1303,8 +1314,8 @@
                         // should not be receiving any input.
                         if (resultType == TO_SPLIT_LEFT_INDICATOR
                                 || resultType == TO_SPLIT_RIGHT_INDICATOR) {
-                            relevantDecor.disposeStatusBarInputLayer();
-                            // We should also dispose the other split task's input layer if
+                            relevantDecor.detachStatusBarInputLayer();
+                            // We should also detach the other split task's input layer if
                             // applicable.
                             final int splitPosition = mSplitScreenController
                                     .getSplitPosition(relevantDecor.mTaskInfo.taskId);
@@ -1317,7 +1328,7 @@
                                         mSplitScreenController.getTaskInfo(oppositePosition);
                                 if (oppositeTaskInfo != null) {
                                     mWindowDecorByTaskId.get(oppositeTaskInfo.taskId)
-                                            .disposeStatusBarInputLayer();
+                                            .detachStatusBarInputLayer();
                                 }
                             }
                         }
@@ -1515,7 +1526,7 @@
                 mTaskOrganizer,
                 windowDecoration,
                 mDisplayController,
-                mDragStartListener,
+                mDragEventListener,
                 mTransitions,
                 mInteractionJankMonitor,
                 mTransactionFactory,
@@ -1529,6 +1540,10 @@
                     touchEventListener.mMotionEvent);
             return Unit.INSTANCE;
         });
+        windowDecoration.setOnImmersiveOrRestoreClickListener(() -> {
+            onEnterOrExitImmersive(taskInfo.taskId);
+            return Unit.INSTANCE;
+        });
         windowDecoration.setOnLeftSnapClickListener(() -> {
             onSnapResize(taskInfo.taskId, /* isLeft= */ true, touchEventListener.mMotionEvent);
             return Unit.INSTANCE;
@@ -1563,6 +1578,7 @@
                 touchEventListener, touchEventListener, touchEventListener, touchEventListener);
         windowDecoration.setExclusionRegionListener(mExclusionRegionListener);
         windowDecoration.setDragPositioningCallback(taskPositioner);
+        windowDecoration.setStatusBarInputLayer(getStatusBarInputLayer(taskInfo));
         windowDecoration.relayout(taskInfo, startT, finishT,
                 false /* applyStartTransactionOnDraw */, false /* shouldSetTaskPositionAndCrop */,
                 mFocusTransitionObserver.hasGlobalFocus(taskInfo));
@@ -1571,6 +1587,18 @@
         }
     }
 
+    /** Decide which cached status bar input layer should be used for a decoration. */
+    private AdditionalSystemViewContainer getStatusBarInputLayer(
+            RunningTaskInfo taskInfo
+    ) {
+        if (mStatusBarInputLayerSupplier == null) return null;
+        return mStatusBarInputLayerSupplier.getStatusBarInputLayer(
+                taskInfo,
+                mSplitScreenController.getSplitPosition(taskInfo.taskId),
+                mSplitScreenController.isLeftRightSplit()
+        );
+    }
+
     private RunningTaskInfo getOtherSplitTask(int taskId) {
         @SplitPosition int remainingTaskPosition = mSplitScreenController
                 .getSplitPosition(taskId) == SPLIT_POSITION_BOTTOM_OR_RIGHT
@@ -1641,13 +1669,18 @@
         }
     }
 
-    private class DragStartListenerImpl
-            implements DragPositioningCallbackUtility.DragStartListener {
+    private class DragEventListenerImpl
+            implements DragPositioningCallbackUtility.DragEventListener {
         @Override
         public void onDragStart(int taskId) {
             final DesktopModeWindowDecoration decoration = mWindowDecorByTaskId.get(taskId);
             decoration.closeHandleMenu();
         }
+
+        @Override
+        public void onDragMove(int taskId) {
+
+        }
     }
 
     /**
@@ -1741,7 +1774,7 @@
                 ShellTaskOrganizer taskOrganizer,
                 DesktopModeWindowDecoration windowDecoration,
                 DisplayController displayController,
-                DragPositioningCallbackUtility.DragStartListener dragStartListener,
+                DragPositioningCallbackUtility.DragEventListener dragEventListener,
                 Transitions transitions,
                 InteractionJankMonitor interactionJankMonitor,
                 Supplier<SurfaceControl.Transaction> transactionFactory,
@@ -1751,7 +1784,7 @@
                             taskOrganizer,
                             windowDecoration,
                             displayController,
-                            dragStartListener,
+                            dragEventListener,
                             transitions,
                             interactionJankMonitor,
                             handler)
@@ -1760,7 +1793,7 @@
                             transitions,
                             windowDecoration,
                             displayController,
-                            dragStartListener,
+                            dragEventListener,
                             transactionFactory);
 
             if (DesktopModeFlags.ENABLE_WINDOWING_SCALED_RESIZING.isTrue()) {
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 d94f3a9..9f37358 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
@@ -27,14 +27,18 @@
 import static android.window.DesktopModeFlags.ENABLE_CAPTION_COMPAT_INSET_FORCE_CONSUMPTION;
 import static android.window.DesktopModeFlags.ENABLE_CAPTION_COMPAT_INSET_FORCE_CONSUMPTION_ALWAYS;
 
+
 import static com.android.launcher3.icons.BaseIconFactory.MODE_DEFAULT;
 import static com.android.wm.shell.shared.desktopmode.DesktopModeStatus.canEnterDesktopMode;
 import static com.android.wm.shell.shared.desktopmode.DesktopModeTransitionSource.APP_HANDLE_MENU_BUTTON;
 import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
+import static com.android.wm.shell.windowdecor.DragResizeWindowGeometry.DisabledEdge;
+import static com.android.wm.shell.windowdecor.DragResizeWindowGeometry.DisabledEdge.NONE;
 import static com.android.wm.shell.windowdecor.DragResizeWindowGeometry.getFineResizeCornerSize;
 import static com.android.wm.shell.windowdecor.DragResizeWindowGeometry.getLargeResizeCornerSize;
 import static com.android.wm.shell.windowdecor.DragResizeWindowGeometry.getResizeEdgeHandleSize;
 import static com.android.wm.shell.windowdecor.DragResizeWindowGeometry.getResizeHandleEdgeInset;
+import static com.android.wm.shell.windowdecor.DragPositioningCallbackUtility.DragEventListener;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -102,6 +106,7 @@
 import com.android.wm.shell.shared.desktopmode.DesktopModeTransitionSource;
 import com.android.wm.shell.shared.desktopmode.ManageWindowsViewContainer;
 import com.android.wm.shell.splitscreen.SplitScreenController;
+import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalSystemViewContainer;
 import com.android.wm.shell.windowdecor.extension.TaskInfoKt;
 import com.android.wm.shell.windowdecor.viewholder.AppHandleViewHolder;
 import com.android.wm.shell.windowdecor.viewholder.AppHeaderViewHolder;
@@ -142,6 +147,7 @@
     private View.OnLongClickListener mOnCaptionLongClickListener;
     private View.OnGenericMotionListener mOnCaptionGenericMotionListener;
     private Function0<Unit> mOnMaximizeOrRestoreClickListener;
+    private Function0<Unit> mOnImmersiveOrRestoreClickListener;
     private Function0<Unit> mOnLeftSnapClickListener;
     private Function0<Unit> mOnRightSnapClickListener;
     private Consumer<DesktopModeTransitionSource> mOnToDesktopClickListener;
@@ -153,6 +159,8 @@
     private DragResizeInputListener mDragResizeListener;
     private Runnable mCurrentViewHostRunnable = null;
     private RelayoutParams mRelayoutParams = new RelayoutParams();
+    private DisabledEdge mDisabledResizingEdge =
+            NONE;
     private final WindowDecoration.RelayoutResult<WindowDecorLinearLayout> mResult =
             new WindowDecoration.RelayoutResult<>();
     private final Runnable mViewHostRunnable =
@@ -198,6 +206,7 @@
     private final MultiInstanceHelper mMultiInstanceHelper;
     private final WindowDecorCaptionHandleRepository mWindowDecorCaptionHandleRepository;
     private final DesktopRepository mDesktopRepository;
+    private AdditionalSystemViewContainer mStatusBarInputLayer;
 
     DesktopModeWindowDecoration(
             Context context,
@@ -291,6 +300,14 @@
         mOnMaximizeOrRestoreClickListener = listener;
     }
 
+    /**
+     * Registers a listener to be called back when one of the tasks' immersive/restore action is
+     * triggered.
+     */
+    void setOnImmersiveOrRestoreClickListener(Function0<Unit> listener) {
+        mOnImmersiveOrRestoreClickListener = listener;
+    }
+
     /** Registers a listener to be called when the decoration's snap-left action is triggered.*/
     void setOnLeftSnapClickListener(Function0<Unit> listener) {
         mOnLeftSnapClickListener = listener;
@@ -318,6 +335,24 @@
         mOnToSplitscreenClickListener = listener;
     }
 
+    /**
+     * Adds a drag resize observer that gets notified on the task being drag resized.
+     *
+     * @param dragResizeListener The observing object to be added.
+     */
+    public void addDragResizeListener(DragEventListener dragResizeListener) {
+        mTaskDragResizer.addDragEventListener(dragResizeListener);
+    }
+
+    /**
+     * Removes an already existing drag resize observer.
+     *
+     * @param dragResizeListener observer to be removed.
+     */
+    public void removeDragResizeListener(DragEventListener dragResizeListener) {
+        mTaskDragResizer.removeDragEventListener(dragResizeListener);
+    }
+
     /** Registers a listener to be called when the decoration's new window action is triggered. */
     void setOnNewWindowClickListener(Function0<Unit> listener) {
         mOnNewWindowClickListener = listener;
@@ -375,6 +410,24 @@
         }
     }
 
+    /**
+     * Disables resizing for the given edge.
+     *
+     * @param disabledResizingEdge edge to disable.
+     * @param shouldDelayUpdate whether the update should be executed immediately or delayed.
+     */
+    public void updateDisabledResizingEdge(
+            DragResizeWindowGeometry.DisabledEdge disabledResizingEdge, boolean shouldDelayUpdate) {
+        mDisabledResizingEdge = disabledResizingEdge;
+        final boolean inFullImmersive = mDesktopRepository
+                .isTaskInFullImmersiveState(mTaskInfo.taskId);
+        if (shouldDelayUpdate) {
+            return;
+        }
+        updateDragResizeListener(mDecorationContainerSurface, inFullImmersive);
+    }
+
+
     void relayout(ActivityManager.RunningTaskInfo taskInfo,
             SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT,
             boolean applyStartTransactionOnDraw, boolean shouldSetTaskPositionAndCrop,
@@ -497,13 +550,13 @@
                 notifyNoCaptionHandle();
             }
             mExclusionRegionListener.onExclusionRegionDismissed(mTaskInfo.taskId);
-            disposeStatusBarInputLayer();
+            detachStatusBarInputLayer();
             Trace.endSection(); // DesktopModeWindowDecoration#updateRelayoutParamsAndSurfaces
             return;
         }
 
         if (oldRootView != mResult.mRootView) {
-            disposeStatusBarInputLayer();
+            detachStatusBarInputLayer();
             mWindowDecorViewHolder = createViewHolder();
         }
         Trace.beginSection("DesktopModeWindowDecoration#relayout-binding");
@@ -521,6 +574,9 @@
                     mTaskInfo, position, mResult.mCaptionWidth, mResult.mCaptionHeight,
                     isCaptionVisible()
             ));
+            if (mStatusBarInputLayer != null) {
+                asAppHandle(mWindowDecorViewHolder).bindStatusBarInputLayer(mStatusBarInputLayer);
+            }
         } else {
             mWindowDecorViewHolder.bindData(new AppHeaderViewHolder.HeaderData(
                     mTaskInfo,
@@ -631,7 +687,8 @@
                 new DragResizeWindowGeometry(mRelayoutParams.mCornerRadius,
                         new Size(mResult.mWidth, mResult.mHeight),
                         getResizeEdgeHandleSize(res), getResizeHandleEdgeInset(res),
-                        getFineResizeCornerSize(res), getLargeResizeCornerSize(res)), touchSlop)
+                        getFineResizeCornerSize(res), getLargeResizeCornerSize(res),
+                        mDisabledResizingEdge), touchSlop)
                 || !mTaskInfo.positionInParent.equals(mPositionInParent)) {
             updateExclusionRegion(inFullImmersive);
         }
@@ -713,7 +770,8 @@
         if (!mTaskInfo.isVisible()) {
             closeMaximizeMenu();
         } else {
-            mMaximizeMenu.positionMenu(calculateMaximizeMenuPosition(), startT);
+            final int menuWidth = calculateMaximizeMenuWidth();
+            mMaximizeMenu.positionMenu(calculateMaximizeMenuPosition(menuWidth), startT);
         }
     }
 
@@ -732,15 +790,15 @@
     }
 
     /**
-     * Dispose of the view used to forward inputs in status bar region. Intended to be
+     * Detach the status bar input layer from this decoration. Intended to be
      * used any time handle is no longer visible.
      */
-    void disposeStatusBarInputLayer() {
+    void detachStatusBarInputLayer() {
         if (!isAppHandle(mWindowDecorViewHolder)
                 || !Flags.enableHandleInputFix()) {
             return;
         }
-        asAppHandle(mWindowDecorViewHolder).disposeStatusBarInputLayer();
+        asAppHandle(mWindowDecorViewHolder).detachStatusBarInputLayer();
     }
 
     private WindowDecorationViewHolder createViewHolder() {
@@ -934,8 +992,27 @@
         return Resources.ID_NULL;
     }
 
+    private int calculateMaximizeMenuWidth() {
+        final boolean showImmersive = Flags.enableFullyImmersiveInDesktop()
+                && TaskInfoKt.getRequestingImmersive(mTaskInfo);
+        final boolean showMaximize = true;
+        final boolean showSnaps = mTaskInfo.isResizeable;
+        int showCount = 0;
+        if (showImmersive) showCount++;
+        if (showMaximize) showCount++;
+        if (showSnaps) showCount++;
+        return switch (showCount) {
+            case 1 -> loadDimensionPixelSize(mContext.getResources(),
+                    R.dimen.desktop_mode_maximize_menu_width_one_options);
+            case 2 -> loadDimensionPixelSize(mContext.getResources(),
+                    R.dimen.desktop_mode_maximize_menu_width_two_options);
+            case 3 -> loadDimensionPixelSize(mContext.getResources(),
+                    R.dimen.desktop_mode_maximize_menu_width_three_options);
+            default -> throw new IllegalArgumentException("");
+        };
+    }
 
-    private PointF calculateMaximizeMenuPosition() {
+    private PointF calculateMaximizeMenuPosition(int menuWidth) {
         final PointF position = new PointF();
         final Resources resources = mContext.getResources();
         final DisplayLayout displayLayout =
@@ -951,8 +1028,6 @@
         final int[] maximizeButtonLocation = new int[2];
         maximizeWindowButton.getLocationInWindow(maximizeButtonLocation);
 
-        final int menuWidth = loadDimensionPixelSize(
-                resources, R.dimen.desktop_mode_maximize_menu_width);
         final int menuHeight = loadDimensionPixelSize(
                 resources, R.dimen.desktop_mode_maximize_menu_height);
 
@@ -1183,11 +1258,19 @@
      * Create and display maximize menu window
      */
     void createMaximizeMenu() {
+        final int menuWidth = calculateMaximizeMenuWidth();
         mMaximizeMenu = mMaximizeMenuFactory.create(mSyncQueue, mRootTaskDisplayAreaOrganizer,
                 mDisplayController, mTaskInfo, mContext,
-                calculateMaximizeMenuPosition(), mSurfaceControlTransactionSupplier);
+                calculateMaximizeMenuPosition(menuWidth), mSurfaceControlTransactionSupplier);
         mMaximizeMenu.show(
+                /* isTaskInImmersiveMode= */ Flags.enableFullyImmersiveInDesktop()
+                        && mDesktopRepository.isTaskInFullImmersiveState(mTaskInfo.taskId),
+                /* menuWidth= */ menuWidth,
+                /* showImmersiveOption= */ Flags.enableFullyImmersiveInDesktop()
+                        && TaskInfoKt.getRequestingImmersive(mTaskInfo),
+                /* showSnapOptions= */ mTaskInfo.isResizeable,
                 mOnMaximizeOrRestoreClickListener,
+                mOnImmersiveOrRestoreClickListener,
                 mOnLeftSnapClickListener,
                 mOnRightSnapClickListener,
                 hovered -> {
@@ -1369,7 +1452,7 @@
 
     void closeManageWindowsMenu() {
         if (mManageWindowsMenu != null) {
-            mManageWindowsMenu.close();
+            mManageWindowsMenu.animateClose();
         }
         mManageWindowsMenu = null;
     }
@@ -1428,19 +1511,6 @@
         }
     }
 
-    /**
-     * Close an open maximize menu if input is outside of menu coordinates
-     *
-     * @param ev the tapped point to compare against
-     */
-    void closeMaximizeMenuIfNeeded(MotionEvent ev) {
-        if (!isMaximizeMenuActive()) return;
-
-        if (!mMaximizeMenu.isValidMenuInput(ev)) {
-            closeMaximizeMenu();
-        }
-    }
-
     boolean isFocused() {
         return mHasGlobalFocus;
     }
@@ -1568,7 +1638,7 @@
         closeManageWindowsMenu();
         mExclusionRegionListener.onExclusionRegionDismissed(mTaskInfo.taskId);
         disposeResizeVeil();
-        disposeStatusBarInputLayer();
+        detachStatusBarInputLayer();
         clearCurrentViewHostRunnable();
         if (canEnterDesktopMode(mContext) && Flags.enableDesktopWindowingAppHandleEducation()) {
             notifyNoCaptionHandle();
@@ -1685,6 +1755,16 @@
                 + "}";
     }
 
+    /**
+     * Set the view container to be used to forward input through status bar. Null in cases
+     * where input forwarding isn't needed.
+     */
+    public void setStatusBarInputLayer(
+            @Nullable AdditionalSystemViewContainer additionalSystemViewContainer
+    ) {
+        mStatusBarInputLayer = additionalSystemViewContainer;
+    }
+
     static class Factory {
 
         DesktopModeWindowDecoration create(
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopStatusBarInputLayerSupplier.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopStatusBarInputLayerSupplier.kt
new file mode 100644
index 0000000..9c5215d
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopStatusBarInputLayerSupplier.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.wm.shell.windowdecor
+
+import android.app.ActivityManager.RunningTaskInfo
+import android.app.WindowConfiguration
+import android.content.Context
+import android.graphics.PixelFormat
+import android.os.Handler
+import android.view.Gravity
+import android.view.View
+import android.view.WindowManager
+import com.android.wm.shell.shared.annotations.ShellMainThread
+import com.android.wm.shell.shared.split.SplitScreenConstants
+import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalSystemViewContainer
+
+/**
+ * Supplier for [AdditionalSystemViewContainer] objects to be used for forwarding input
+ * events through status bar to an app handle. Currently supports two simultaneous input layers.
+ *
+ * The supplier will pick one of two input layer view containers to use: one for tasks in
+ * fullscreen or top/left split stage, and one for tasks in right split stage.
+ */
+class DesktopStatusBarInputLayerSupplier(
+    private val context: Context,
+    @ShellMainThread handler: Handler
+) {
+    private val inputLayers: MutableList<AdditionalSystemViewContainer> = mutableListOf()
+
+    init {
+        // Post this as creation of the input layer views is a relatively expensive operation.
+        handler.post {
+            repeat(TOTAL_INPUT_LAYERS) {
+                inputLayers.add(createInputLayer())
+            }
+        }
+    }
+
+    private fun createInputLayer(): AdditionalSystemViewContainer {
+        val lp = WindowManager.LayoutParams(
+            WindowManager.LayoutParams.TYPE_STATUS_BAR_ADDITIONAL,
+            WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
+            PixelFormat.TRANSPARENT
+        )
+        lp.title = "Desktop status bar input layer"
+        lp.gravity = Gravity.LEFT or Gravity.TOP
+        lp.setTrustedOverlay()
+
+        // Make this window a spy window to enable it to pilfer pointers from the system-wide
+        // gesture listener that receives events before window. This is to prevent notification
+        // shade gesture when we swipe down to enter desktop.
+        lp.inputFeatures = WindowManager.LayoutParams.INPUT_FEATURE_SPY
+        lp.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+        val view = View(context)
+        view.visibility = View.GONE
+        return AdditionalSystemViewContainer(
+            WindowManagerWrapper(
+                context.getSystemService<WindowManager>(WindowManager::class.java)
+            ),
+            view,
+            lp
+        )
+    }
+
+    /**
+     * Decide which cached status bar input layer should be used for a decoration, if any.
+     *
+     * [splitPosition] and [isLeftRightSplit] are used to determine which input layer we use.
+     * The first one is reserved for fullscreen tasks or tasks in top/left split,
+     * while the second one is exclusively used for tasks in right split stage. Note we care about
+     * left-right vs top-bottom split as the bottom stage should not use an input layer.
+     */
+    fun getStatusBarInputLayer(
+        taskInfo: RunningTaskInfo,
+        @SplitScreenConstants.SplitPosition splitPosition: Int,
+        isLeftRightSplit: Boolean
+    ): AdditionalSystemViewContainer? {
+        if (!taskInfo.isVisibleRequested) return null
+        // Fullscreen and top/left split tasks will use the first input layer.
+        if (taskInfo.windowingMode == WindowConfiguration.WINDOWING_MODE_FULLSCREEN
+            || splitPosition == SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT
+        ) {
+            return inputLayers[LEFT_TOP_INPUT_LAYER]
+        }
+        // Right split tasks will use the second one.
+        if (isLeftRightSplit && splitPosition == SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT
+        ) {
+            return inputLayers[RIGHT_SPLIT_INPUT_LAYER]
+        }
+        // Which leaves bottom split and freeform tasks, which do not need an input layer
+        // as the status bar is not blocking them.
+        return null
+    }
+
+    companion object {
+        private const val TOTAL_INPUT_LAYERS = 2
+        // Input layer index for fullscreen tasks and tasks in top-left split
+        private const val LEFT_TOP_INPUT_LAYER = 0
+        // Input layer index for tasks in right split stage. Does not include bottom split as that
+        // stage is not blocked by status bar.
+        private const val RIGHT_SPLIT_INPUT_LAYER = 1
+    }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragDetector.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragDetector.java
index 01bb7f7..d36fc12 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragDetector.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragDetector.java
@@ -42,7 +42,7 @@
  *
  * All touch events must be passed through this class to track a drag event.
  */
-class DragDetector {
+public class DragDetector {
     private final MotionEventHandler mEventHandler;
 
     private final PointF mInputDownPoint = new PointF();
@@ -55,7 +55,14 @@
 
     private boolean mResultOfDownAction;
 
-    DragDetector(@NonNull MotionEventHandler eventHandler, long holdToDragMinDurationMs,
+    /**
+     * Initialises a drag detector.
+     *
+     * @param eventHandler drag event handler.
+     * @param holdToDragMinDurationMs hold to drag duration.
+     * @param touchSlop touch slope threshold.
+     */
+    public DragDetector(@NonNull MotionEventHandler eventHandler, long holdToDragMinDurationMs,
             int touchSlop) {
         resetState();
         mEventHandler = eventHandler;
@@ -69,7 +76,7 @@
      * @return the result returned by {@link #mEventHandler}, or the result when
      * {@link #mEventHandler} handles the previous down event if the event shouldn't be passed
      */
-    boolean onMotionEvent(MotionEvent ev) {
+    public boolean onMotionEvent(MotionEvent ev) {
         return onMotionEvent(null /* view */, ev);
     }
 
@@ -79,7 +86,7 @@
      * @return the result returned by {@link #mEventHandler}, or the result when
      * {@link #mEventHandler} handles the previous down event if the event shouldn't be passed
      */
-    boolean onMotionEvent(View v, MotionEvent ev) {
+    public boolean onMotionEvent(View v, MotionEvent ev) {
         final boolean isTouchScreen =
                 (ev.getSource() & SOURCE_TOUCHSCREEN) == SOURCE_TOUCHSCREEN;
         if (!isTouchScreen) {
@@ -190,7 +197,16 @@
         mDidHoldForMinDuration = false;
     }
 
-    interface MotionEventHandler {
+    /**
+     * Interface to be implemented by the class using the DragDetector for callback.
+     */
+    public interface MotionEventHandler {
+        /**
+         * Called back when drag is detected to notify the implementing class to handle drag events.
+         * @param v view on which the input arrived.
+         * @param ev motion event that resulted in drag.
+         * @return whether this was a drag event or not.
+         */
         boolean handleMotionEvent(@Nullable View v, MotionEvent ev);
     }
 }
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragPositioningCallbackUtility.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragPositioningCallbackUtility.java
index 78e7962..8eced3e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragPositioningCallbackUtility.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragPositioningCallbackUtility.java
@@ -208,7 +208,17 @@
         return result;
     }
 
-    private static boolean isExceedingWidthConstraint(int repositionedWidth, int startingWidth,
+    /**
+     * Checks whether the new task bounds exceed the allowed width.
+     *
+     * @param repositionedWidth task width after repositioning.
+     * @param startingWidth task width before repositioning.
+     * @param maxResizeBounds stable bounds for display.
+     * @param displayController display controller for the task being checked.
+     * @param windowDecoration contains decor info and helpers for the task.
+     * @return whether the task is exceeding any of the width constrains, minimum or maximum.
+     */
+    public static boolean isExceedingWidthConstraint(int repositionedWidth, int startingWidth,
             Rect maxResizeBounds, DisplayController displayController,
             WindowDecoration windowDecoration) {
         boolean isSizeIncreasing = (repositionedWidth - startingWidth) > 0;
@@ -223,7 +233,17 @@
                 && repositionedWidth > maxResizeBounds.width() && isSizeIncreasing;
     }
 
-    private static boolean isExceedingHeightConstraint(int repositionedHeight, int startingHeight,
+    /**
+     * Checks whether the new task bounds exceed the allowed height.
+     *
+     * @param repositionedHeight task's height after repositioning.
+     * @param startingHeight task's height before repositioning.
+     * @param maxResizeBounds stable bounds for display.
+     * @param displayController display controller for the task being checked.
+     * @param windowDecoration contains decor info and helpers for the task.
+     * @return whether the task is exceeding any of the height constrains, minimum or maximum.
+     */
+    public static boolean isExceedingHeightConstraint(int repositionedHeight, int startingHeight,
             Rect maxResizeBounds, DisplayController displayController,
             WindowDecoration windowDecoration) {
         boolean isSizeIncreasing = (repositionedHeight - startingHeight) > 0;
@@ -284,12 +304,19 @@
                 && DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_SIZE_CONSTRAINTS.isTrue();
     }
 
-    interface DragStartListener {
+    public interface DragEventListener {
         /**
          * Inform the implementing class that a drag resize has started
          *
          * @param taskId id of this positioner's {@link WindowDecoration}
          */
         void onDragStart(int taskId);
+
+        /**
+         * Inform the implementing class that a drag move has started.
+         *
+         * @param taskId id of this positioner's {@link WindowDecoration}
+         */
+        void onDragMove(int taskId);
     }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeWindowGeometry.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeWindowGeometry.java
index 844ceb3..6f72d34 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeWindowGeometry.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeWindowGeometry.java
@@ -42,7 +42,7 @@
 /**
  * Geometry for a drag resize region for a particular window.
  */
-final class DragResizeWindowGeometry {
+public final class DragResizeWindowGeometry {
     private final int mTaskCornerRadius;
     private final Size mTaskSize;
     // The size of the handle outside the task window applied to the edges of the window, for the
@@ -58,19 +58,24 @@
     // The bounds for each edge drag region, which can resize the task in one direction.
     final @NonNull TaskEdges mTaskEdges;
 
+    private final DisabledEdge mDisabledEdge;
+
     DragResizeWindowGeometry(int taskCornerRadius, @NonNull Size taskSize,
             int resizeHandleEdgeOutset, int resizeHandleEdgeInset, int fineCornerSize,
-            int largeCornerSize) {
+            int largeCornerSize, DisabledEdge disabledEdge) {
         mTaskCornerRadius = taskCornerRadius;
         mTaskSize = taskSize;
         mResizeHandleEdgeOutset = resizeHandleEdgeOutset;
         mResizeHandleEdgeInset = resizeHandleEdgeInset;
 
-        mLargeTaskCorners = new TaskCorners(mTaskSize, largeCornerSize);
-        mFineTaskCorners = new TaskCorners(mTaskSize, fineCornerSize);
+        mDisabledEdge = disabledEdge;
+
+        mLargeTaskCorners = new TaskCorners(mTaskSize, largeCornerSize, disabledEdge);
+        mFineTaskCorners = new TaskCorners(mTaskSize, fineCornerSize, disabledEdge);
 
         // Save touch areas for each edge.
-        mTaskEdges = new TaskEdges(mTaskSize, mResizeHandleEdgeOutset, mResizeHandleEdgeInset);
+        mTaskEdges = new TaskEdges(mTaskSize, mResizeHandleEdgeOutset, mResizeHandleEdgeInset,
+                mDisabledEdge);
     }
 
     /**
@@ -170,7 +175,7 @@
                     || e.getToolType(0) == MotionEvent.TOOL_TYPE_MOUSE
                     // Touchpad input
                     || (e.isFromSource(SOURCE_MOUSE)
-                        && e.getToolType(0) == MotionEvent.TOOL_TYPE_FINGER);
+                    && e.getToolType(0) == MotionEvent.TOOL_TYPE_FINGER);
         } else {
             return e.getToolType(0) == MotionEvent.TOOL_TYPE_MOUSE;
         }
@@ -187,8 +192,9 @@
     /**
      * Returns the control type for the drag-resize, based on the touch regions and this
      * MotionEvent's coordinates.
+     *
      * @param isTouchscreen Controls the size of the corner resize regions; touchscreen events
-     *                      (finger & stylus) are eligible for a larger area than cursor events
+     *                      (finger & stylus) are eligible for a larger area than cursor events.
      * @param isEdgeResizePermitted Indicates if the event is eligible for falling into an edge
      *                              resize region.
      */
@@ -252,6 +258,10 @@
     @DragPositioningCallback.CtrlType
     private int checkDistanceFromCenter(@DragPositioningCallback.CtrlType int ctrlType, float x,
             float y) {
+        if ((mDisabledEdge == DisabledEdge.RIGHT && (ctrlType & CTRL_TYPE_RIGHT) != 0)
+                || mDisabledEdge == DisabledEdge.LEFT && ((ctrlType & CTRL_TYPE_LEFT) != 0)) {
+            return CTRL_TYPE_UNDEFINED;
+        }
         final Point cornerRadiusCenter = calculateCenterForCornerRadius(ctrlType);
         double distanceFromCenter = Math.hypot(x - cornerRadiusCenter.x, y - cornerRadiusCenter.y);
 
@@ -337,29 +347,31 @@
         private final @NonNull Rect mRightTopCornerBounds;
         private final @NonNull Rect mLeftBottomCornerBounds;
         private final @NonNull Rect mRightBottomCornerBounds;
+        private final @NonNull DisabledEdge mDisabledEdge;
 
-        TaskCorners(@NonNull Size taskSize, int cornerSize) {
+        TaskCorners(@NonNull Size taskSize, int cornerSize, DisabledEdge disabledEdge) {
             mCornerSize = cornerSize;
+            mDisabledEdge = disabledEdge;
             final int cornerRadius = cornerSize / 2;
-            mLeftTopCornerBounds = new Rect(
+            mLeftTopCornerBounds = (disabledEdge == DisabledEdge.LEFT) ? new Rect() : new Rect(
                     -cornerRadius,
                     -cornerRadius,
                     cornerRadius,
                     cornerRadius);
 
-            mRightTopCornerBounds = new Rect(
+            mRightTopCornerBounds = (disabledEdge == DisabledEdge.RIGHT) ? new Rect() : new Rect(
                     taskSize.getWidth() - cornerRadius,
                     -cornerRadius,
                     taskSize.getWidth() + cornerRadius,
                     cornerRadius);
 
-            mLeftBottomCornerBounds = new Rect(
+            mLeftBottomCornerBounds = (disabledEdge == DisabledEdge.LEFT) ? new Rect() : new Rect(
                     -cornerRadius,
                     taskSize.getHeight() - cornerRadius,
                     cornerRadius,
                     taskSize.getHeight() + cornerRadius);
 
-            mRightBottomCornerBounds = new Rect(
+            mRightBottomCornerBounds = (disabledEdge == DisabledEdge.RIGHT) ? new Rect() : new Rect(
                     taskSize.getWidth() - cornerRadius,
                     taskSize.getHeight() - cornerRadius,
                     taskSize.getWidth() + cornerRadius,
@@ -370,10 +382,14 @@
          * Updates the region to include all four corners.
          */
         void union(Region region) {
-            region.union(mLeftTopCornerBounds);
-            region.union(mRightTopCornerBounds);
-            region.union(mLeftBottomCornerBounds);
-            region.union(mRightBottomCornerBounds);
+            if (mDisabledEdge != DisabledEdge.RIGHT) {
+                region.union(mRightTopCornerBounds);
+                region.union(mRightBottomCornerBounds);
+            }
+            if (mDisabledEdge != DisabledEdge.LEFT) {
+                region.union(mLeftTopCornerBounds);
+                region.union(mLeftBottomCornerBounds);
+            }
         }
 
         /**
@@ -440,9 +456,12 @@
         private final @NonNull Rect mRightEdgeBounds;
         private final @NonNull Rect mBottomEdgeBounds;
         private final @NonNull Region mRegion;
+        private final @NonNull DisabledEdge mDisabledEdge;
 
         private TaskEdges(@NonNull Size taskSize, int resizeHandleThickness,
-                int resizeHandleEdgeInset) {
+                int resizeHandleEdgeInset, DisabledEdge disabledEdge) {
+            // Save touch areas for each edge.
+            mDisabledEdge = disabledEdge;
             // Save touch areas for each edge.
             mTopEdgeBounds = new Rect(
                     -resizeHandleThickness,
@@ -466,10 +485,7 @@
                     taskSize.getHeight() + resizeHandleThickness);
 
             mRegion = new Region();
-            mRegion.union(mTopEdgeBounds);
-            mRegion.union(mLeftEdgeBounds);
-            mRegion.union(mRightEdgeBounds);
-            mRegion.union(mBottomEdgeBounds);
+            union(mRegion);
         }
 
         /**
@@ -483,9 +499,13 @@
          * Updates the region to include all four corners.
          */
         private void union(Region region) {
+            if (mDisabledEdge != DisabledEdge.RIGHT) {
+                region.union(mRightEdgeBounds);
+            }
+            if (mDisabledEdge != DisabledEdge.LEFT) {
+                region.union(mLeftEdgeBounds);
+            }
             region.union(mTopEdgeBounds);
-            region.union(mLeftEdgeBounds);
-            region.union(mRightEdgeBounds);
             region.union(mBottomEdgeBounds);
         }
 
@@ -519,4 +539,10 @@
                     mBottomEdgeBounds);
         }
     }
+
+    public enum DisabledEdge {
+        LEFT,
+        RIGHT,
+        NONE
+    }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositioner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositioner.java
index ccf329c..3efae9d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositioner.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositioner.java
@@ -35,6 +35,7 @@
 import com.android.wm.shell.common.DisplayController;
 import com.android.wm.shell.transition.Transitions;
 
+import java.util.ArrayList;
 import java.util.function.Supplier;
 
 /**
@@ -55,7 +56,8 @@
     private final WindowDecoration mWindowDecoration;
     private final Supplier<SurfaceControl.Transaction> mTransactionSupplier;
     private DisplayController mDisplayController;
-    private DragPositioningCallbackUtility.DragStartListener mDragStartListener;
+    private ArrayList<DragPositioningCallbackUtility.DragEventListener> mDragEventListeners =
+            new ArrayList<>();
     private final Rect mStableBounds = new Rect();
     private final Rect mTaskBoundsAtDragStart = new Rect();
     private final PointF mRepositionStartPoint = new PointF();
@@ -69,20 +71,22 @@
     FluidResizeTaskPositioner(ShellTaskOrganizer taskOrganizer, Transitions transitions,
             WindowDecoration windowDecoration, DisplayController displayController) {
         this(taskOrganizer, transitions, windowDecoration, displayController,
-                dragStartListener -> {}, SurfaceControl.Transaction::new);
+                null, SurfaceControl.Transaction::new);
     }
 
     FluidResizeTaskPositioner(ShellTaskOrganizer taskOrganizer,
             Transitions transitions,
             WindowDecoration windowDecoration,
             DisplayController displayController,
-            DragPositioningCallbackUtility.DragStartListener dragStartListener,
+            DragPositioningCallbackUtility.DragEventListener dragEventListener,
             Supplier<SurfaceControl.Transaction> supplier) {
         mTaskOrganizer = taskOrganizer;
         mTransitions = transitions;
         mWindowDecoration = windowDecoration;
         mDisplayController = displayController;
-        mDragStartListener = dragStartListener;
+        if (dragEventListener != null) {
+            mDragEventListeners.add(dragEventListener);
+        }
         mTransactionSupplier = supplier;
     }
 
@@ -92,7 +96,9 @@
         mTaskBoundsAtDragStart.set(
                 mWindowDecoration.mTaskInfo.configuration.windowConfiguration.getBounds());
         mRepositionStartPoint.set(x, y);
-        mDragStartListener.onDragStart(mWindowDecoration.mTaskInfo.taskId);
+        for (DragPositioningCallbackUtility.DragEventListener listener : mDragEventListeners) {
+            listener.onDragStart(mWindowDecoration.mTaskInfo.taskId);
+        }
         if (mCtrlType != CTRL_TYPE_UNDEFINED && !mWindowDecoration.mHasGlobalFocus) {
             WindowContainerTransaction wct = new WindowContainerTransaction();
             wct.reorder(mWindowDecoration.mTaskInfo.token, true /* onTop */,
@@ -120,6 +126,10 @@
             // The task is being resized, send the |dragResizing| hint to core with the first
             // bounds-change wct.
             if (!mHasDragResized) {
+                for (DragPositioningCallbackUtility.DragEventListener listener :
+                        mDragEventListeners) {
+                    listener.onDragMove(mWindowDecoration.mTaskInfo.taskId);
+                }
                 // This is the first bounds change since drag resize operation started.
                 wct.setDragResizing(mWindowDecoration.mTaskInfo.token, true /* dragResizing */);
             }
@@ -216,4 +226,16 @@
     public boolean isResizingOrAnimating() {
         return mIsResizingOrAnimatingResize;
     }
+
+    @Override
+    public void addDragEventListener(
+            DragPositioningCallbackUtility.DragEventListener dragEventListener) {
+        mDragEventListeners.add(dragEventListener);
+    }
+
+    @Override
+    public void removeDragEventListener(
+            DragPositioningCallbackUtility.DragEventListener dragEventListener) {
+        mDragEventListeners.remove(dragEventListener);
+    }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeMenu.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeMenu.kt
index 3ae5a1a..4bb1e7b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeMenu.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeMenu.kt
@@ -36,7 +36,6 @@
 import android.graphics.drawable.shapes.RoundRectShape
 import android.util.StateSet
 import android.view.LayoutInflater
-import android.view.MotionEvent
 import android.view.MotionEvent.ACTION_HOVER_ENTER
 import android.view.MotionEvent.ACTION_HOVER_EXIT
 import android.view.MotionEvent.ACTION_HOVER_MOVE
@@ -58,6 +57,8 @@
 import androidx.compose.material3.ColorScheme
 import androidx.compose.ui.graphics.toArgb
 import androidx.core.animation.addListener
+import androidx.core.view.isGone
+import androidx.core.view.isVisible
 import com.android.wm.shell.R
 import com.android.wm.shell.RootTaskDisplayAreaOrganizer
 import com.android.wm.shell.common.DisplayController
@@ -93,7 +94,6 @@
     private val cornerRadius = loadDimensionPixelSize(
             R.dimen.desktop_mode_maximize_menu_corner_radius
     ).toFloat()
-    private val menuWidth = loadDimensionPixelSize(R.dimen.desktop_mode_maximize_menu_width)
     private val menuHeight = loadDimensionPixelSize(R.dimen.desktop_mode_maximize_menu_height)
     private val menuPadding = loadDimensionPixelSize(R.dimen.desktop_mode_menu_padding)
 
@@ -105,7 +105,12 @@
 
     /** Creates and shows the maximize window. */
     fun show(
+        isTaskInImmersiveMode: Boolean,
+        menuWidth: Int,
+        showImmersiveOption: Boolean,
+        showSnapOptions: Boolean,
         onMaximizeOrRestoreClickListener: () -> Unit,
+        onImmersiveOrRestoreClickListener: () -> Unit,
         onLeftSnapClickListener: () -> Unit,
         onRightSnapClickListener: () -> Unit,
         onHoverListener: (Boolean) -> Unit,
@@ -113,7 +118,12 @@
     ) {
         if (maximizeMenu != null) return
         createMaximizeMenu(
+            isTaskInImmersiveMode = isTaskInImmersiveMode,
+            menuWidth = menuWidth,
+            showImmersiveOption = showImmersiveOption,
+            showSnapOptions = showSnapOptions,
             onMaximizeClickListener = onMaximizeOrRestoreClickListener,
+            onImmersiveOrRestoreClickListener = onImmersiveOrRestoreClickListener,
             onLeftSnapClickListener = onLeftSnapClickListener,
             onRightSnapClickListener = onRightSnapClickListener,
             onHoverListener = onHoverListener,
@@ -144,7 +154,12 @@
 
     /** Create a maximize menu that is attached to the display area. */
     private fun createMaximizeMenu(
+        isTaskInImmersiveMode: Boolean,
+        menuWidth: Int,
+        showImmersiveOption: Boolean,
+        showSnapOptions: Boolean,
         onMaximizeClickListener: () -> Unit,
+        onImmersiveOrRestoreClickListener: () -> Unit,
         onLeftSnapClickListener: () -> Unit,
         onRightSnapClickListener: () -> Unit,
         onHoverListener: (Boolean) -> Unit,
@@ -179,11 +194,20 @@
         maximizeMenuView = MaximizeMenuView(
             context = decorWindowContext,
             sizeToggleDirection = getSizeToggleDirection(),
+            immersiveConfig = if (showImmersiveOption) {
+                MaximizeMenuView.ImmersiveConfig.Visible(
+                    getImmersiveToggleDirection(isTaskInImmersiveMode)
+                )
+            } else {
+                MaximizeMenuView.ImmersiveConfig.Hidden
+            },
+            showSnapOptions = showSnapOptions,
             menuHeight = menuHeight,
             menuPadding = menuPadding,
         ).also { menuView ->
             menuView.bind(taskInfo)
             menuView.onMaximizeClickListener = onMaximizeClickListener
+            menuView.onImmersiveOrRestoreClickListener = onImmersiveOrRestoreClickListener
             menuView.onLeftSnapClickListener = onLeftSnapClickListener
             menuView.onRightSnapClickListener = onRightSnapClickListener
             menuView.onMenuHoverListener = onHoverListener
@@ -217,6 +241,15 @@
             MaximizeMenuView.SizeToggleDirection.MAXIMIZE
     }
 
+    private fun getImmersiveToggleDirection(
+        isTaskImmersive: Boolean
+    ): MaximizeMenuView.ImmersiveToggleDirection =
+        if (isTaskImmersive) {
+            MaximizeMenuView.ImmersiveToggleDirection.EXIT
+        } else {
+            MaximizeMenuView.ImmersiveToggleDirection.ENTER
+        }
+
     private fun loadDimensionPixelSize(resourceId: Int): Int {
         return if (resourceId == Resources.ID_NULL) {
             0
@@ -226,33 +259,14 @@
     }
 
     /**
-     * A valid menu input is one of the following:
-     * An input that happens in the menu views.
-     * Any input before the views have been laid out.
-     *
-     * @param inputPoint the input to compare against.
-     */
-    fun isValidMenuInput(ev: MotionEvent): Boolean {
-        val x = ev.rawX
-        val y = ev.rawY
-        return !viewsLaidOut() || (menuPosition.x <= x && menuPosition.x + menuWidth >= x &&
-                menuPosition.y <= y && menuPosition.y + menuHeight >= y)
-    }
-
-    /**
-     * Check if the views for maximize menu can be seen.
-     */
-    private fun viewsLaidOut(): Boolean {
-        return maximizeMenu?.view?.isLaidOut ?: false
-    }
-
-    /**
      * The view within the Maximize Menu, presents maximize, restore and snap-to-side options for
      * resizing a Task.
      */
     class MaximizeMenuView(
-        private val context: Context,
+        context: Context,
         private val sizeToggleDirection: SizeToggleDirection,
+        immersiveConfig: ImmersiveConfig,
+        showSnapOptions: Boolean,
         private val menuHeight: Int,
         private val menuPadding: Int
     ) {
@@ -260,10 +274,20 @@
             .inflate(R.layout.desktop_mode_window_decor_maximize_menu, null /* root */) as ViewGroup
         private val container = requireViewById(R.id.container)
         private val overlay = requireViewById(R.id.maximize_menu_overlay)
+        private val immersiveToggleContainer =
+            requireViewById(R.id.maximize_menu_immersive_toggle_container) as View
+        private val immersiveToggleButtonText =
+            requireViewById(R.id.maximize_menu_immersive_toggle_button_text) as TextView
+        private val immersiveToggleButton =
+            requireViewById(R.id.maximize_menu_immersive_toggle_button) as Button
+        private val sizeToggleContainer =
+            requireViewById(R.id.maximize_menu_size_toggle_container) as View
         private val sizeToggleButtonText =
             requireViewById(R.id.maximize_menu_size_toggle_button_text) as TextView
         private val sizeToggleButton =
             requireViewById(R.id.maximize_menu_size_toggle_button) as Button
+        private val snapContainer =
+            requireViewById(R.id.maximize_menu_snap_container) as View
         private val snapWindowText =
             requireViewById(R.id.maximize_menu_snap_window_text) as TextView
         private val snapRightButton =
@@ -282,6 +306,35 @@
         private val fillRadius = context.resources
             .getDimensionPixelSize(R.dimen.desktop_mode_maximize_menu_buttons_fill_radius)
 
+        private val immersiveFillPadding = context.resources.getDimensionPixelSize(R.dimen
+            .desktop_mode_maximize_menu_immersive_button_fill_padding)
+        private val maximizeFillPaddingDefault = context.resources.getDimensionPixelSize(R.dimen
+            .desktop_mode_maximize_menu_snap_and_maximize_buttons_fill_padding)
+        private val maximizeFillPaddingBottom = context.resources.getDimensionPixelSize(R.dimen
+            .desktop_mode_maximize_menu_snap_and_maximize_buttons_fill_padding_bottom)
+        private val maximizeRestoreFillPaddingVertical = context.resources.getDimensionPixelSize(
+            R.dimen.desktop_mode_maximize_menu_restore_button_fill_vertical_padding)
+        private val maximizeRestoreFillPaddingHorizontal = context.resources.getDimensionPixelSize(
+            R.dimen.desktop_mode_maximize_menu_restore_button_fill_horizontal_padding)
+        private val maximizeFillPaddingRect = Rect(
+            maximizeFillPaddingDefault,
+            maximizeFillPaddingDefault,
+            maximizeFillPaddingDefault,
+            maximizeFillPaddingBottom
+        )
+        private val maximizeRestoreFillPaddingRect = Rect(
+            maximizeRestoreFillPaddingHorizontal,
+            maximizeRestoreFillPaddingVertical,
+            maximizeRestoreFillPaddingHorizontal,
+            maximizeRestoreFillPaddingVertical,
+        )
+        private val immersiveFillPaddingRect = Rect(
+            immersiveFillPadding,
+            immersiveFillPadding,
+            immersiveFillPadding,
+            immersiveFillPadding
+        )
+
         private val hoverTempRect = Rect()
         private var menuAnimatorSet: AnimatorSet? = null
         private lateinit var taskInfo: RunningTaskInfo
@@ -289,6 +342,8 @@
 
         /** Invoked when the maximize or restore option is clicked. */
         var onMaximizeClickListener: (() -> Unit)? = null
+        /** Invoked when the immersive or restore option is clicked. */
+        var onImmersiveOrRestoreClickListener: (() -> Unit)? = null
         /** Invoked when the left snap option is clicked. */
         var onLeftSnapClickListener: (() -> Unit)? = null
         /** Invoked when the right snap option is clicked. */
@@ -338,6 +393,11 @@
                 return@setOnHoverListener false
             }
 
+            immersiveToggleContainer.isGone = immersiveConfig is ImmersiveConfig.Hidden
+            sizeToggleContainer.isVisible = true
+            snapContainer.isGone = !showSnapOptions
+
+            immersiveToggleButton.setOnClickListener { onImmersiveOrRestoreClickListener?.invoke() }
             sizeToggleButton.setOnClickListener { onMaximizeClickListener?.invoke() }
             snapRightButton.setOnClickListener { onRightSnapClickListener?.invoke() }
             snapLeftButton.setOnClickListener { onLeftSnapClickListener?.invoke() }
@@ -349,17 +409,36 @@
                 true
             }
 
-            val btnTextId = if (sizeToggleDirection == SizeToggleDirection.RESTORE)
+            // Maximize/restore button.
+            val sizeToggleBtnTextId = if (sizeToggleDirection == SizeToggleDirection.RESTORE)
                 R.string.desktop_mode_maximize_menu_restore_button_text
             else
                 R.string.desktop_mode_maximize_menu_maximize_button_text
-            val btnText = context.resources.getText(btnTextId)
-            sizeToggleButton.contentDescription = btnText
-            sizeToggleButtonText.text = btnText
+            val sizeToggleBtnText = context.resources.getText(sizeToggleBtnTextId)
+            sizeToggleButton.contentDescription = sizeToggleBtnText
+            sizeToggleButtonText.text = sizeToggleBtnText
+
+            // Immersive enter/exit button.
+            if (immersiveConfig is ImmersiveConfig.Visible) {
+                val immersiveToggleBtnTextId = when (immersiveConfig.direction) {
+                    ImmersiveToggleDirection.ENTER -> {
+                        R.string.desktop_mode_maximize_menu_immersive_button_text
+                    }
+
+                    ImmersiveToggleDirection.EXIT -> {
+                        R.string.desktop_mode_maximize_menu_immersive_restore_button_text
+                    }
+                }
+                val immersiveToggleBtnText = context.resources.getText(immersiveToggleBtnTextId)
+                immersiveToggleButton.contentDescription = immersiveToggleBtnText
+                immersiveToggleButtonText.text = immersiveToggleBtnText
+            }
 
             // To prevent aliasing.
             sizeToggleButton.setLayerType(View.LAYER_TYPE_SOFTWARE, null)
             sizeToggleButtonText.setLayerType(View.LAYER_TYPE_SOFTWARE, null)
+            immersiveToggleButton.setLayerType(View.LAYER_TYPE_SOFTWARE, null)
+            immersiveToggleButtonText.setLayerType(View.LAYER_TYPE_SOFTWARE, null)
         }
 
         /** Bind the menu views to the new [RunningTaskInfo] data. */
@@ -373,6 +452,10 @@
             sizeToggleButton.background = style.maximizeOption.drawable
             sizeToggleButtonText.setTextColor(style.textColor)
 
+            // Immersive option.
+            immersiveToggleButton.background = style.immersiveOption.drawable
+            immersiveToggleButtonText.setTextColor(style.textColor)
+
             // Snap options.
             snapWindowText.setTextColor(style.textColor)
             updateSplitSnapSelection(SnapToHalfSelection.NONE)
@@ -382,6 +465,8 @@
         fun animateOpenMenu(onEnd: () -> Unit) {
             sizeToggleButton.setLayerType(View.LAYER_TYPE_HARDWARE, null)
             sizeToggleButtonText.setLayerType(View.LAYER_TYPE_HARDWARE, null)
+            immersiveToggleButton.setLayerType(View.LAYER_TYPE_HARDWARE, null)
+            immersiveToggleButtonText.setLayerType(View.LAYER_TYPE_HARDWARE, null)
             menuAnimatorSet = AnimatorSet()
             menuAnimatorSet?.playTogether(
                 ObjectAnimator.ofFloat(rootView, SCALE_Y, STARTING_MENU_HEIGHT_SCALE, 1f)
@@ -411,8 +496,10 @@
                         // scale is cancelled out and only the background is scaled.
                         val value = animatedValue as Float
                         sizeToggleButton.scaleY = value
+                        immersiveToggleButton.scaleY = value
                         snapButtonsLayout.scaleY = value
                         sizeToggleButtonText.scaleY = value
+                        immersiveToggleButtonText.scaleY = value
                         snapWindowText.scaleY = value
                     }
                 },
@@ -432,8 +519,10 @@
                         addUpdateListener {
                             val value = animatedValue as Float
                             sizeToggleButton.alpha = value
+                            immersiveToggleButton.alpha = value
                             snapButtonsLayout.alpha = value
                             sizeToggleButtonText.alpha = value
+                            immersiveToggleButtonText.alpha = value
                             snapWindowText.alpha = value
                         }
                     },
@@ -447,6 +536,8 @@
                 onEnd = {
                     sizeToggleButton.setLayerType(View.LAYER_TYPE_SOFTWARE, null)
                     sizeToggleButtonText.setLayerType(View.LAYER_TYPE_SOFTWARE, null)
+                    immersiveToggleButton.setLayerType(View.LAYER_TYPE_SOFTWARE, null)
+                    immersiveToggleButtonText.setLayerType(View.LAYER_TYPE_SOFTWARE, null)
                     onEnd.invoke()
                 }
             )
@@ -457,6 +548,8 @@
         fun animateCloseMenu(onEnd: (() -> Unit)) {
             sizeToggleButton.setLayerType(View.LAYER_TYPE_HARDWARE, null)
             sizeToggleButtonText.setLayerType(View.LAYER_TYPE_HARDWARE, null)
+            immersiveToggleButton.setLayerType(View.LAYER_TYPE_HARDWARE, null)
+            immersiveToggleButtonText.setLayerType(View.LAYER_TYPE_HARDWARE, null)
             cancelAnimation()
             menuAnimatorSet = AnimatorSet()
             menuAnimatorSet?.playTogether(
@@ -487,8 +580,10 @@
                             // scale is cancelled out and only the background is scaled.
                             val value = animatedValue as Float
                             sizeToggleButton.scaleY = value
+                            immersiveToggleButton.scaleY = value
                             snapButtonsLayout.scaleY = value
                             sizeToggleButtonText.scaleY = value
+                            immersiveToggleButtonText.scaleY = value
                             snapWindowText.scaleY = value
                         }
                     },
@@ -508,8 +603,10 @@
                                 addUpdateListener {
                                     val value = animatedValue as Float
                                     sizeToggleButton.alpha = value
+                                    immersiveToggleButton.alpha = value
                                     snapButtonsLayout.alpha = value
                                     sizeToggleButtonText.alpha = value
+                                    immersiveToggleButtonText.alpha = value
                                     snapWindowText.alpha = value
                                 }
                             },
@@ -522,6 +619,8 @@
                     onEnd = {
                         sizeToggleButton.setLayerType(View.LAYER_TYPE_SOFTWARE, null)
                         sizeToggleButtonText.setLayerType(View.LAYER_TYPE_SOFTWARE, null)
+                        immersiveToggleButton.setLayerType(View.LAYER_TYPE_SOFTWARE, null)
+                        immersiveToggleButtonText.setLayerType(View.LAYER_TYPE_SOFTWARE, null)
                         onEnd?.invoke()
                     }
             )
@@ -531,6 +630,14 @@
         /** Request that the accessibility service focus on the menu. */
         fun requestAccessibilityFocus() {
             // Focus the first button in the menu by default.
+            if (immersiveToggleButton.isVisible) {
+                immersiveToggleButton.post {
+                    immersiveToggleButton.sendAccessibilityEvent(
+                        AccessibilityEvent.TYPE_VIEW_FOCUSED
+                    )
+                }
+                return
+            }
             sizeToggleButton.post {
                 sizeToggleButton.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED)
             }
@@ -557,7 +664,21 @@
                 backgroundColor = menuBackgroundColor,
                 textColor = colorScheme.onSurface.toArgb(),
                 maximizeOption = MenuStyle.MaximizeOption(
-                    drawable = createMaximizeDrawable(menuBackgroundColor, colorScheme)
+                    drawable = createMaximizeOrImmersiveDrawable(
+                        menuBackgroundColor,
+                        colorScheme,
+                        fillPadding = when (sizeToggleDirection) {
+                            SizeToggleDirection.MAXIMIZE -> maximizeFillPaddingRect
+                            SizeToggleDirection.RESTORE -> maximizeRestoreFillPaddingRect
+                        }
+                    )
+                ),
+                immersiveOption = MenuStyle.ImmersiveOption(
+                    drawable = createMaximizeOrImmersiveDrawable(
+                        menuBackgroundColor,
+                        colorScheme,
+                        fillPadding = immersiveFillPaddingRect,
+                    ),
                 ),
                 snapOptions = MenuStyle.SnapOptions(
                     inactiveSnapSideColor = colorScheme.outlineVariant.toArgb(),
@@ -624,19 +745,21 @@
             }
         }
 
-        private fun createMaximizeDrawable(
+        private fun createMaximizeOrImmersiveDrawable(
             @ColorInt menuBackgroundColor: Int,
-            colorScheme: ColorScheme
+            colorScheme: ColorScheme,
+            fillPadding: Rect,
         ): StateListDrawable {
             val activeStrokeAndFill = colorScheme.primary.toArgb()
             val activeBackground = colorScheme.primary.toArgb().withAlpha(OPACITY_12)
-            val activeDrawable = createMaximizeButtonDrawable(
+            val activeDrawable = createMaximizeOrImmersiveButtonDrawable(
                 strokeAndFillColor = activeStrokeAndFill,
                 backgroundColor = activeBackground,
                 // Add a mask with the menu background's color because the active background color is
                 // semi transparent, otherwise the transparency will reveal the stroke/fill color
                 // behind it.
-                backgroundMask = menuBackgroundColor
+                backgroundMask = menuBackgroundColor,
+                fillPadding = fillPadding,
             )
             return StateListDrawable().apply {
                 addState(intArrayOf(android.R.attr.state_pressed), activeDrawable)
@@ -646,19 +769,21 @@
                 // Inactive drawable.
                 addState(
                     StateSet.WILD_CARD,
-                    createMaximizeButtonDrawable(
+                    createMaximizeOrImmersiveButtonDrawable(
                         strokeAndFillColor = colorScheme.outlineVariant.toArgb(),
                         backgroundColor = colorScheme.surfaceContainerLow.toArgb(),
-                        backgroundMask = null // not needed because the bg color is fully opaque
+                        backgroundMask = null, // not needed because the bg color is fully opaque
+                        fillPadding = fillPadding,
                     )
                 )
             }
         }
 
-        private fun createMaximizeButtonDrawable(
+        private fun createMaximizeOrImmersiveButtonDrawable(
             @ColorInt strokeAndFillColor: Int,
             @ColorInt backgroundColor: Int,
-            @ColorInt backgroundMask: Int?
+            @ColorInt backgroundMask: Int?,
+            fillPadding: Rect,
         ): LayerDrawable {
             val layers = mutableListOf<Drawable>()
             // First (bottom) layer, effectively the button's border ring once its inner shape is
@@ -708,30 +833,17 @@
                 paint.style = Paint.Style.FILL
             })
 
-            val (horizontalFillPadding, verticalFillPadding) =
-                if (sizeToggleDirection == SizeToggleDirection.MAXIMIZE) {
-                    context.resources.getDimensionPixelSize(R.dimen
-                        .desktop_mode_maximize_menu_snap_and_maximize_buttons_fill_padding) to
-                            context.resources.getDimensionPixelSize(R.dimen
-                                .desktop_mode_maximize_menu_snap_and_maximize_buttons_fill_padding)
-                } else {
-                    context.resources.getDimensionPixelSize(R.dimen
-                        .desktop_mode_maximize_menu_restore_button_fill_horizontal_padding) to
-                            context.resources.getDimensionPixelSize(R.dimen
-                                .desktop_mode_maximize_menu_restore_button_fill_vertical_padding)
-                }
-
             return LayerDrawable(layers.toTypedArray()).apply {
                 when (numberOfLayers) {
                     3 -> {
                         setLayerInset(1, outlineStroke)
-                        setLayerInset(2, horizontalFillPadding, verticalFillPadding,
-                            horizontalFillPadding, verticalFillPadding)
+                        setLayerInset(2, fillPadding.left, fillPadding.top,
+                            fillPadding.right, fillPadding.bottom)
                     }
                     4 -> {
                         setLayerInset(intArrayOf(1, 2), outlineStroke)
-                        setLayerInset(3, horizontalFillPadding, verticalFillPadding,
-                            horizontalFillPadding, verticalFillPadding)
+                        setLayerInset(3, fillPadding.left, fillPadding.top,
+                            fillPadding.right, fillPadding.bottom)
                     }
                     else -> error("Unexpected number of layers: $numberOfLayers")
                 }
@@ -755,11 +867,15 @@
             @ColorInt val backgroundColor: Int,
             @ColorInt val textColor: Int,
             val maximizeOption: MaximizeOption,
+            val immersiveOption: ImmersiveOption,
             val snapOptions: SnapOptions,
         ) {
             data class MaximizeOption(
                 val drawable: StateListDrawable,
             )
+            data class ImmersiveOption(
+                val drawable: StateListDrawable,
+            )
             data class SnapOptions(
                 @ColorInt val inactiveSnapSideColor: Int,
                 @ColorInt val semiActiveSnapSideColor: Int,
@@ -776,10 +892,23 @@
             NONE, LEFT, RIGHT
         }
 
+        /** The possible immersive configs for this menu instance. */
+        sealed class ImmersiveConfig {
+            data class Visible(
+                val direction: ImmersiveToggleDirection,
+            ) : ImmersiveConfig()
+            data object Hidden : ImmersiveConfig()
+        }
+
         /** The possible selection states of the size toggle button in the maximize menu. */
         enum class SizeToggleDirection {
             MAXIMIZE, RESTORE
         }
+
+        /** The possible selection states of the immersive toggle button in the maximize menu. */
+        enum class ImmersiveToggleDirection {
+            ENTER, EXIT
+        }
     }
 
     companion object {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/ResizeVeil.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/ResizeVeil.kt
index fb81ed4..8770d35 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/ResizeVeil.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/ResizeVeil.kt
@@ -49,7 +49,7 @@
 /**
  * Creates and updates a veil that covers task contents on resize.
  */
-class ResizeVeil @JvmOverloads constructor(
+public class ResizeVeil @JvmOverloads constructor(
         private val context: Context,
         private val displayController: DisplayController,
         private val appIcon: Bitmap,
@@ -188,28 +188,16 @@
             t.apply()
             return
         }
-        isVisible = true
         val background = backgroundSurface
         val icon = iconSurface
-        val veil = veilSurface
-        if (background == null || icon == null || veil == null) return
-
-        // Parent surface can change, ensure it is up to date.
-        if (parent != parentSurface) {
-            t.reparent(veil, parent)
-            parentSurface = parent
-        }
-
-        val backgroundColor = when (decorThemeUtil.getAppTheme(taskInfo)) {
-            Theme.LIGHT -> lightColors.surfaceContainer
-            Theme.DARK -> darkColors.surfaceContainer
-        }
-        t.show(veil)
-                .setLayer(veil, VEIL_CONTAINER_LAYER)
-                .setLayer(icon, VEIL_ICON_LAYER)
-                .setLayer(background, VEIL_BACKGROUND_LAYER)
-                .setColor(background, Color.valueOf(backgroundColor.toArgb()).components)
-        relayout(taskBounds, t)
+        if (background == null || icon == null) return
+        updateTransactionWithShowVeil(
+            t,
+            parent,
+            taskBounds,
+            taskInfo,
+            fadeIn,
+        )
         if (fadeIn) {
             cancelAnimation()
             val veilAnimT = surfaceControlTransactionSupplier.get()
@@ -259,11 +247,43 @@
             iconAnimator.start()
         } else {
             // Show the veil immediately.
+            t.apply()
+        }
+    }
+
+    fun updateTransactionWithShowVeil(
+        t: SurfaceControl.Transaction,
+        parent: SurfaceControl,
+        taskBounds: Rect,
+        taskInfo: RunningTaskInfo,
+        fadeIn: Boolean = false,
+    ) {
+        if (!isReady || isVisible) return
+        isVisible = true
+        val background = backgroundSurface
+        val icon = iconSurface
+        val veil = veilSurface
+        if (background == null || icon == null || veil == null) return
+        // Parent surface can change, ensure it is up to date.
+        if (parent != parentSurface) {
+            t.reparent(veil, parent)
+            parentSurface = parent
+        }
+        val backgroundColor = when (decorThemeUtil.getAppTheme(taskInfo)) {
+            Theme.LIGHT -> lightColors.surfaceContainer
+            Theme.DARK -> darkColors.surfaceContainer
+        }
+        t.show(veil)
+            .setLayer(veil, VEIL_CONTAINER_LAYER)
+            .setLayer(icon, VEIL_ICON_LAYER)
+            .setLayer(background, VEIL_BACKGROUND_LAYER)
+            .setColor(background, Color.valueOf(backgroundColor.toArgb()).components)
+        relayout(taskBounds, t)
+        if (!fadeIn) {
             t.show(icon)
-                    .show(background)
-                    .setAlpha(icon, 1f)
-                    .setAlpha(background, 1f)
-                    .apply()
+                .show(background)
+                .setAlpha(icon, 1f)
+                .setAlpha(background, 1f)
         }
     }
 
@@ -314,8 +334,12 @@
      * @param newBounds bounds to update veil to.
      */
     fun updateResizeVeil(t: SurfaceControl.Transaction, newBounds: Rect) {
+        updateTransactionWithResizeVeil(t, newBounds)
+        t.apply()
+    }
+
+    fun updateTransactionWithResizeVeil(t: SurfaceControl.Transaction, newBounds: Rect) {
         if (!isVisible) {
-            t.apply()
             return
         }
         veilAnimator?.let { animator ->
@@ -325,7 +349,6 @@
             }
         }
         relayout(newBounds, t)
-        t.apply()
     }
 
     /**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskDragResizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskDragResizer.java
index d7ea0c3..63b288d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskDragResizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskDragResizer.java
@@ -26,4 +26,19 @@
      * a resize is complete.
      */
     boolean isResizingOrAnimating();
+
+    /**
+     * Adds a drag start listener to be notified of drag start events.
+     *
+     * @param dragEventListener Listener to be added.
+     */
+    void addDragEventListener(DragPositioningCallbackUtility.DragEventListener dragEventListener);
+
+    /**
+     * Removes a drag start listener from the listener set.
+     *
+     * @param dragEventListener Listener to be removed.
+     */
+    void removeDragEventListener(
+            DragPositioningCallbackUtility.DragEventListener dragEventListener);
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositioner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositioner.java
index ff3b455..a1e329a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositioner.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositioner.java
@@ -43,6 +43,7 @@
 import com.android.wm.shell.shared.annotations.ShellMainThread;
 import com.android.wm.shell.transition.Transitions;
 
+import java.util.ArrayList;
 import java.util.function.Supplier;
 
 /**
@@ -56,7 +57,8 @@
     private DesktopModeWindowDecoration mDesktopWindowDecoration;
     private ShellTaskOrganizer mTaskOrganizer;
     private DisplayController mDisplayController;
-    private DragPositioningCallbackUtility.DragStartListener mDragStartListener;
+    private ArrayList<DragPositioningCallbackUtility.DragEventListener>
+            mDragEventListeners = new ArrayList<>();
     private final Transitions mTransitions;
     private final Rect mStableBounds = new Rect();
     private final Rect mTaskBoundsAtDragStart = new Rect();
@@ -73,23 +75,23 @@
     public VeiledResizeTaskPositioner(ShellTaskOrganizer taskOrganizer,
             DesktopModeWindowDecoration windowDecoration,
             DisplayController displayController,
-            DragPositioningCallbackUtility.DragStartListener dragStartListener,
+            DragPositioningCallbackUtility.DragEventListener dragEventListener,
             Transitions transitions, InteractionJankMonitor interactionJankMonitor,
             @ShellMainThread Handler handler) {
-        this(taskOrganizer, windowDecoration, displayController, dragStartListener,
+        this(taskOrganizer, windowDecoration, displayController, dragEventListener,
                 SurfaceControl.Transaction::new, transitions, interactionJankMonitor, handler);
     }
 
     public VeiledResizeTaskPositioner(ShellTaskOrganizer taskOrganizer,
             DesktopModeWindowDecoration windowDecoration,
             DisplayController displayController,
-            DragPositioningCallbackUtility.DragStartListener dragStartListener,
+            DragPositioningCallbackUtility.DragEventListener dragEventListener,
             Supplier<SurfaceControl.Transaction> supplier, Transitions transitions,
             InteractionJankMonitor interactionJankMonitor, @ShellMainThread Handler handler) {
         mDesktopWindowDecoration = windowDecoration;
         mTaskOrganizer = taskOrganizer;
         mDisplayController = displayController;
-        mDragStartListener = dragStartListener;
+        mDragEventListeners.add(dragEventListener);
         mTransactionSupplier = supplier;
         mTransitions = transitions;
         mInteractionJankMonitor = interactionJankMonitor;
@@ -113,7 +115,10 @@
                 mTaskOrganizer.applyTransaction(wct);
             }
         }
-        mDragStartListener.onDragStart(mDesktopWindowDecoration.mTaskInfo.taskId);
+        for (DragPositioningCallbackUtility.DragEventListener dragEventListener :
+                mDragEventListeners) {
+            dragEventListener.onDragStart(mDesktopWindowDecoration.mTaskInfo.taskId);
+        }
         mRepositionTaskBounds.set(mTaskBoundsAtDragStart);
         int rotation = mDesktopWindowDecoration
                 .mTaskInfo.configuration.windowConfiguration.getDisplayRotation();
@@ -137,6 +142,10 @@
                 mRepositionTaskBounds, mTaskBoundsAtDragStart, mStableBounds, delta,
                 mDisplayController, mDesktopWindowDecoration)) {
             if (!mIsResizingOrAnimatingResize) {
+                for (DragPositioningCallbackUtility.DragEventListener dragEventListener :
+                        mDragEventListeners) {
+                    dragEventListener.onDragMove(mDesktopWindowDecoration.mTaskInfo.taskId);
+                }
                 mDesktopWindowDecoration.showResizeVeil(mRepositionTaskBounds);
                 mIsResizingOrAnimatingResize = true;
             } else {
@@ -237,4 +246,16 @@
     public boolean isResizingOrAnimating() {
         return mIsResizingOrAnimatingResize;
     }
+
+    @Override
+    public void addDragEventListener(
+            DragPositioningCallbackUtility.DragEventListener dragEventListener) {
+        mDragEventListeners.add(dragEventListener);
+    }
+
+    @Override
+    public void removeDragEventListener(
+            DragPositioningCallbackUtility.DragEventListener dragEventListener) {
+        mDragEventListeners.remove(dragEventListener);
+    }
 }
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 34cc098..f97dfb89 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
@@ -131,12 +131,15 @@
                 }
             };
 
-    RunningTaskInfo mTaskInfo;
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+    public RunningTaskInfo mTaskInfo;
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+    public Context mDecorWindowContext;
     int mLayoutResId;
-    final SurfaceControl mTaskSurface;
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+    public final SurfaceControl mTaskSurface;
 
     Display mDisplay;
-    Context mDecorWindowContext;
     SurfaceControl mDecorationContainerSurface;
 
     SurfaceControl mCaptionContainerSurface;
@@ -200,6 +203,14 @@
     }
 
     /**
+     * Gets the decoration's task leash.
+     * @return the decoration' task surface used to manipulate the task.
+     */
+    public SurfaceControl getLeash() {
+        return mTaskSurface;
+    }
+
+    /**
      * Used by {@link WindowDecoration} to trigger a new relayout because the requirements for a
      * relayout weren't satisfied are satisfied now.
      *
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/additionalviewcontainer/AdditionalSystemViewContainer.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/additionalviewcontainer/AdditionalSystemViewContainer.kt
index 8b6aaaf..1451f36 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/additionalviewcontainer/AdditionalSystemViewContainer.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/additionalviewcontainer/AdditionalSystemViewContainer.kt
@@ -23,8 +23,8 @@
 import android.view.LayoutInflater
 import android.view.SurfaceControl
 import android.view.View
-import android.view.WindowInsets
 import android.view.WindowManager
+import android.view.WindowManager.LayoutParams
 import com.android.wm.shell.windowdecor.WindowManagerWrapper
 
 /**
@@ -33,27 +33,11 @@
  */
 class AdditionalSystemViewContainer(
     private val windowManagerWrapper: WindowManagerWrapper,
-    taskId: Int,
-    x: Int,
-    y: Int,
-    width: Int,
-    height: Int,
-    flags: Int,
-    @WindowInsets.Type.InsetsType forciblyShownTypes: Int = 0,
-    override val view: View
+    override val view: View,
+    val lp: LayoutParams
 ) : AdditionalViewContainer() {
-    val lp: WindowManager.LayoutParams = WindowManager.LayoutParams(
-        width, height, x, y,
-        WindowManager.LayoutParams.TYPE_STATUS_BAR_ADDITIONAL,
-        flags,
-        PixelFormat.TRANSPARENT
-    ).apply {
-        title = "Additional view container of Task=$taskId"
-        gravity = Gravity.LEFT or Gravity.TOP
-        setTrustedOverlay()
-        this.forciblyShownTypes = forciblyShownTypes
-    }
 
+    /** Provide a layout id of a view to inflate for this view container. */
     constructor(
         context: Context,
         windowManagerWrapper: WindowManagerWrapper,
@@ -66,15 +50,30 @@
         @LayoutRes layoutId: Int
     ) : this(
         windowManagerWrapper = windowManagerWrapper,
-        taskId = taskId,
-        x = x,
-        y = y,
-        width = width,
-        height = height,
-        flags = flags,
-        view = LayoutInflater.from(context).inflate(layoutId, null /* parent */)
+        view = LayoutInflater.from(context).inflate(layoutId, null /* parent */),
+        lp = createLayoutParams(x, y, width, height, flags, taskId)
     )
 
+    /** Provide a view directly for this view container */
+    constructor(
+        windowManagerWrapper: WindowManagerWrapper,
+        taskId: Int,
+        x: Int,
+        y: Int,
+        width: Int,
+        height: Int,
+        flags: Int,
+        view: View,
+        forciblyShownTypes: Int = 0
+    ) : this(
+        windowManagerWrapper = windowManagerWrapper,
+        view = view,
+        lp = createLayoutParams(x, y, width, height, flags, taskId).apply {
+            this.forciblyShownTypes = forciblyShownTypes
+        }
+    )
+
+    /** Do not supply a view at all, instead creating the view container with a basic view. */
     constructor(
         context: Context,
         windowManagerWrapper: WindowManagerWrapper,
@@ -86,12 +85,7 @@
         flags: Int
     ) : this(
         windowManagerWrapper = windowManagerWrapper,
-        taskId = taskId,
-        x = x,
-        y = y,
-        width = width,
-        height = height,
-        flags = flags,
+        lp = createLayoutParams(x, y, width, height, flags, taskId),
         view = View(context)
     )
 
@@ -104,7 +98,7 @@
     }
 
     override fun setPosition(t: SurfaceControl.Transaction, x: Float, y: Float) {
-        val lp = (view.layoutParams as WindowManager.LayoutParams).apply {
+        lp.apply {
             this.x = x.toInt()
             this.y = y.toInt()
         }
@@ -124,13 +118,29 @@
         ): AdditionalSystemViewContainer =
             AdditionalSystemViewContainer(
                 windowManagerWrapper = windowManagerWrapper,
-                taskId = taskId,
-                x = x,
-                y = y,
-                width = width,
-                height = height,
-                flags = flags,
-                view = view
+                view = view,
+                lp = createLayoutParams(x, y, width, height, flags, taskId)
             )
     }
+    companion object {
+        fun createLayoutParams(
+            x: Int,
+            y: Int,
+            width: Int,
+            height: Int,
+            flags: Int,
+            taskId: Int
+        ): LayoutParams {
+            return LayoutParams(
+                width, height, x, y,
+                LayoutParams.TYPE_STATUS_BAR_ADDITIONAL,
+                flags,
+                PixelFormat.TRANSPARENT
+            ).apply {
+                title = "Additional view container of Task=$taskId"
+                gravity = Gravity.LEFT or Gravity.TOP
+                setTrustedOverlay()
+            }
+        }
+    }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDecorViewModel.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDecorViewModel.kt
new file mode 100644
index 0000000..ff418c6
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDecorViewModel.kt
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.windowdecor.tiling
+
+import android.app.ActivityManager
+import android.app.ActivityManager.RunningTaskInfo
+import android.content.Context
+import android.graphics.Rect
+import android.util.SparseArray
+import androidx.core.util.valueIterator
+import com.android.internal.annotations.VisibleForTesting
+import com.android.wm.shell.RootTaskDisplayAreaOrganizer
+import com.android.wm.shell.ShellTaskOrganizer
+import com.android.wm.shell.common.DisplayController
+import com.android.wm.shell.common.SyncTransactionQueue
+import com.android.wm.shell.desktopmode.DesktopRepository
+import com.android.wm.shell.desktopmode.DesktopTasksController
+import com.android.wm.shell.desktopmode.ReturnToDragStartAnimator
+import com.android.wm.shell.desktopmode.ToggleResizeDesktopTaskTransitionHandler
+import com.android.wm.shell.transition.Transitions
+import com.android.wm.shell.windowdecor.DesktopModeWindowDecoration
+
+/** Manages tiling for each displayId/userId independently. */
+class DesktopTilingDecorViewModel(
+    private val context: Context,
+    private val displayController: DisplayController,
+    private val rootTdaOrganizer: RootTaskDisplayAreaOrganizer,
+    private val syncQueue: SyncTransactionQueue,
+    private val transitions: Transitions,
+    private val shellTaskOrganizer: ShellTaskOrganizer,
+    private val toggleResizeDesktopTaskTransitionHandler: ToggleResizeDesktopTaskTransitionHandler,
+    private val returnToDragStartAnimator: ReturnToDragStartAnimator,
+    private val taskRepository: DesktopRepository,
+) {
+    @VisibleForTesting
+    var tilingTransitionHandlerByDisplayId = SparseArray<DesktopTilingWindowDecoration>()
+
+    fun snapToHalfScreen(
+        taskInfo: ActivityManager.RunningTaskInfo,
+        desktopModeWindowDecoration: DesktopModeWindowDecoration,
+        position: DesktopTasksController.SnapPosition,
+        destinationBounds: Rect,
+    ): Boolean {
+        val displayId = taskInfo.displayId
+        val handler =
+            tilingTransitionHandlerByDisplayId.get(displayId)
+                ?: run {
+                    val newHandler =
+                        DesktopTilingWindowDecoration(
+                            context,
+                            syncQueue,
+                            displayController,
+                            displayId,
+                            rootTdaOrganizer,
+                            transitions,
+                            shellTaskOrganizer,
+                            toggleResizeDesktopTaskTransitionHandler,
+                            returnToDragStartAnimator,
+                            taskRepository,
+                        )
+                    tilingTransitionHandlerByDisplayId.put(displayId, newHandler)
+                    newHandler
+                }
+        transitions.registerObserver(handler)
+        return handler.onAppTiled(
+            taskInfo,
+            desktopModeWindowDecoration,
+            position,
+            destinationBounds,
+        )
+    }
+
+    fun removeTaskIfTiled(displayId: Int, taskId: Int) {
+        tilingTransitionHandlerByDisplayId.get(displayId)?.removeTaskIfTiled(taskId)
+    }
+
+    fun moveTaskToFrontIfTiled(taskInfo: RunningTaskInfo): Boolean {
+        return tilingTransitionHandlerByDisplayId
+            .get(taskInfo.displayId)
+            ?.moveTiledPairToFront(taskInfo) ?: false
+    }
+
+    fun onOverviewAnimationStateChange(isRunning: Boolean) {
+        for (tilingHandler in tilingTransitionHandlerByDisplayId.valueIterator()) {
+            tilingHandler.onOverviewAnimationStateChange(isRunning)
+        }
+    }
+
+    fun onUserChange() {
+        for (tilingHandler in tilingTransitionHandlerByDisplayId.valueIterator()) {
+            tilingHandler.onUserChange()
+        }
+    }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDividerWindowManager.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDividerWindowManager.kt
new file mode 100644
index 0000000..9bf1304
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDividerWindowManager.kt
@@ -0,0 +1,228 @@
+/*
+ * 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.windowdecor.tiling
+
+import android.content.Context
+import android.content.res.Configuration
+import android.graphics.PixelFormat
+import android.graphics.Rect
+import android.graphics.Region
+import android.os.Binder
+import android.view.LayoutInflater
+import android.view.SurfaceControl
+import android.view.SurfaceControlViewHost
+import android.view.View
+import android.view.WindowManager
+import android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+import android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
+import android.view.WindowManager.LayoutParams.FLAG_SLIPPERY
+import android.view.WindowManager.LayoutParams.FLAG_SPLIT_TOUCH
+import android.view.WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
+import android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION
+import android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY
+import android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER
+import android.view.WindowlessWindowManager
+import com.android.wm.shell.R
+import com.android.wm.shell.common.SyncTransactionQueue
+import java.util.function.Supplier
+
+/**
+ * a [WindowlessWindowManaer] responsible for hosting the [TilingDividerView] on the display root
+ * when two tasks are tiled on left and right to resize them simultaneously.
+ */
+class DesktopTilingDividerWindowManager(
+    private val config: Configuration,
+    private val windowName: String,
+    private val context: Context,
+    private val leash: SurfaceControl,
+    private val syncQueue: SyncTransactionQueue,
+    private val transitionHandler: DesktopTilingWindowDecoration,
+    private val transactionSupplier: Supplier<SurfaceControl.Transaction>,
+    private var dividerBounds: Rect,
+) : WindowlessWindowManager(config, leash, null), DividerMoveCallback, View.OnLayoutChangeListener {
+    private lateinit var viewHost: SurfaceControlViewHost
+    private var tilingDividerView: TilingDividerView? = null
+    private var dividerShown = false
+    private var handleRegionWidth: Int = -1
+    private var setTouchRegion = true
+
+    /**
+     * Gets bounds of divider window with screen based coordinate on the param Rect.
+     *
+     * @param rect bounds for the [TilingDividerView]
+     */
+    fun getDividerBounds(rect: Rect) {
+        rect.set(dividerBounds)
+    }
+
+    /** Sets the touch region for the SurfaceControlViewHost. */
+    fun setTouchRegion(region: Rect) {
+        setTouchRegion(viewHost.windowToken.asBinder(), Region(region))
+    }
+
+    /**
+     * Builds a view host upon tiling two tasks left and right, and shows the divider view in the
+     * middle of the screen between both tasks.
+     *
+     * @param relativeLeash the task leash that the TilingDividerView should be shown on top of.
+     */
+    fun generateViewHost(relativeLeash: SurfaceControl) {
+        val t = transactionSupplier.get()
+        val surfaceControlViewHost =
+            SurfaceControlViewHost(context, context.display, this, "DesktopTilingManager")
+        val dividerView =
+            LayoutInflater.from(context).inflate(R.layout.tiling_split_divider, /* root= */ null)
+                as TilingDividerView
+        val lp = getWindowManagerParams()
+        surfaceControlViewHost.setView(dividerView, lp)
+        val tmpDividerBounds = Rect()
+        getDividerBounds(tmpDividerBounds)
+        dividerView.setup(this, tmpDividerBounds)
+        t.setRelativeLayer(leash, relativeLeash, 1)
+            .setPosition(leash, dividerBounds.left.toFloat(), dividerBounds.top.toFloat())
+            .show(leash)
+        syncQueue.runInSync { transaction ->
+            transaction.merge(t)
+            t.close()
+        }
+        dividerShown = true
+        viewHost = surfaceControlViewHost
+        dividerView.addOnLayoutChangeListener(this)
+        tilingDividerView = dividerView
+        handleRegionWidth = dividerView.handleRegionWidth
+    }
+
+    /** Hides the divider bar. */
+    fun hideDividerBar() {
+        if (!dividerShown) {
+            return
+        }
+        val t = transactionSupplier.get()
+        t.hide(leash)
+        t.apply()
+        dividerShown = false
+    }
+
+    /** Shows the divider bar. */
+    fun showDividerBar() {
+        if (dividerShown) return
+        val t = transactionSupplier.get()
+        t.show(leash)
+        t.apply()
+        dividerShown = true
+    }
+
+    /**
+     * When the tiled task on top changes, the divider bar's Z access should change to be on top of
+     * the latest focused task.
+     */
+    fun onRelativeLeashChanged(relativeLeash: SurfaceControl, t: SurfaceControl.Transaction) {
+        t.setRelativeLayer(leash, relativeLeash, 1)
+    }
+
+    override fun onDividerMoveStart(pos: Int) {
+        setSlippery(false)
+    }
+
+    /**
+     * Moves the divider view to a new position after touch, gets called from the
+     * [TilingDividerView] onTouch function.
+     */
+    override fun onDividerMove(pos: Int): Boolean {
+        val t = transactionSupplier.get()
+        t.setPosition(leash, pos.toFloat(), dividerBounds.top.toFloat())
+        val dividerWidth = dividerBounds.width()
+        dividerBounds.set(pos, dividerBounds.top, pos + dividerWidth, dividerBounds.bottom)
+        return transitionHandler.onDividerHandleMoved(dividerBounds, t)
+    }
+
+    /**
+     * Notifies the transition handler of tiling operations ending, which might result in resizing
+     * WindowContainerTransactions if the sizes of the tiled tasks changed.
+     */
+    override fun onDividerMovedEnd(pos: Int) {
+        setSlippery(true)
+        val t = transactionSupplier.get()
+        t.setPosition(leash, pos.toFloat(), dividerBounds.top.toFloat())
+        val dividerWidth = dividerBounds.width()
+        dividerBounds.set(pos, dividerBounds.top, pos + dividerWidth, dividerBounds.bottom)
+        transitionHandler.onDividerHandleDragEnd(dividerBounds, t)
+    }
+
+    private fun getWindowManagerParams(): WindowManager.LayoutParams {
+        val lp =
+            WindowManager.LayoutParams(
+                dividerBounds.width(),
+                dividerBounds.height(),
+                TYPE_DOCK_DIVIDER,
+                FLAG_NOT_FOCUSABLE or
+                    FLAG_NOT_TOUCH_MODAL or
+                    FLAG_WATCH_OUTSIDE_TOUCH or
+                    FLAG_SPLIT_TOUCH or
+                    FLAG_SLIPPERY,
+                PixelFormat.TRANSLUCENT,
+            )
+        lp.token = Binder()
+        lp.title = windowName
+        lp.privateFlags =
+            lp.privateFlags or (PRIVATE_FLAG_NO_MOVE_ANIMATION or PRIVATE_FLAG_TRUSTED_OVERLAY)
+        return lp
+    }
+
+    /**
+     * Releases the surface control of the current [TilingDividerView] and tear down the view
+     * hierarchy.y.
+     */
+    fun release() {
+        tilingDividerView = null
+        viewHost.release()
+        transactionSupplier.get().hide(leash).remove(leash).apply()
+    }
+
+    override fun onLayoutChange(
+        v: View?,
+        left: Int,
+        top: Int,
+        right: Int,
+        bottom: Int,
+        oldLeft: Int,
+        oldTop: Int,
+        oldRight: Int,
+        oldBottom: Int,
+    ) {
+        if (!setTouchRegion) return
+
+        val startX = (dividerBounds.width() - handleRegionWidth) / 2
+        val startY = 0
+        val tempRect = Rect(startX, startY, startX + handleRegionWidth, dividerBounds.height())
+        setTouchRegion(tempRect)
+        setTouchRegion = false
+    }
+
+    private fun setSlippery(slippery: Boolean) {
+        val lp = tilingDividerView?.layoutParams as WindowManager.LayoutParams
+        val isSlippery = (lp.flags and FLAG_SLIPPERY) != 0
+        if (isSlippery == slippery) return
+
+        if (slippery) {
+            lp.flags = lp.flags or FLAG_SLIPPERY
+        } else {
+            lp.flags = lp.flags and FLAG_SLIPPERY.inv()
+        }
+        viewHost.relayout(lp)
+    }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecoration.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecoration.kt
new file mode 100644
index 0000000..8f089dc
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecoration.kt
@@ -0,0 +1,657 @@
+/*
+ * 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.windowdecor.tiling
+
+import android.app.ActivityManager.RunningTaskInfo
+import android.content.Context
+import android.content.res.Configuration
+import android.content.res.Resources
+import android.graphics.Bitmap
+import android.graphics.Rect
+import android.os.IBinder
+import android.util.Slog
+import android.view.SurfaceControl
+import android.view.SurfaceControl.Transaction
+import android.view.WindowManager.TRANSIT_CHANGE
+import android.view.WindowManager.TRANSIT_OPEN
+import android.view.WindowManager.TRANSIT_TO_BACK
+import android.view.WindowManager.TRANSIT_TO_FRONT
+import android.window.TransitionInfo
+import android.window.TransitionRequestInfo
+import android.window.WindowContainerTransaction
+import com.android.internal.annotations.VisibleForTesting
+import com.android.launcher3.icons.BaseIconFactory
+import com.android.launcher3.icons.BaseIconFactory.MODE_DEFAULT
+import com.android.launcher3.icons.IconProvider
+import com.android.wm.shell.R
+import com.android.wm.shell.RootTaskDisplayAreaOrganizer
+import com.android.wm.shell.ShellTaskOrganizer
+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.DesktopRepository
+import com.android.wm.shell.desktopmode.DesktopTasksController.SnapPosition
+import com.android.wm.shell.desktopmode.ReturnToDragStartAnimator
+import com.android.wm.shell.desktopmode.ToggleResizeDesktopTaskTransitionHandler
+import com.android.wm.shell.transition.Transitions
+import com.android.wm.shell.transition.Transitions.TRANSIT_MINIMIZE
+import com.android.wm.shell.windowdecor.DesktopModeWindowDecoration
+import com.android.wm.shell.windowdecor.DragPositioningCallbackUtility
+import com.android.wm.shell.windowdecor.DragPositioningCallbackUtility.DragEventListener
+import com.android.wm.shell.windowdecor.DragResizeWindowGeometry
+import com.android.wm.shell.windowdecor.DragResizeWindowGeometry.DisabledEdge.NONE
+import com.android.wm.shell.windowdecor.ResizeVeil
+import com.android.wm.shell.windowdecor.extension.isFullscreen
+import java.util.function.Supplier
+
+class DesktopTilingWindowDecoration(
+    private var context: Context,
+    private val syncQueue: SyncTransactionQueue,
+    private val displayController: DisplayController,
+    private val displayId: Int,
+    private val rootTdaOrganizer: RootTaskDisplayAreaOrganizer,
+    private val transitions: Transitions,
+    private val shellTaskOrganizer: ShellTaskOrganizer,
+    private val toggleResizeDesktopTaskTransitionHandler: ToggleResizeDesktopTaskTransitionHandler,
+    private val returnToDragStartAnimator: ReturnToDragStartAnimator,
+    private val taskRepository: DesktopRepository,
+    private val transactionSupplier: Supplier<Transaction> = Supplier { Transaction() },
+) :
+    Transitions.TransitionHandler,
+    ShellTaskOrganizer.FocusListener,
+    ShellTaskOrganizer.TaskVanishedListener,
+    DragEventListener,
+    Transitions.TransitionObserver {
+    companion object {
+        private val TAG: String = DesktopTilingWindowDecoration::class.java.simpleName
+        private const val TILING_DIVIDER_TAG = "Tiling Divider"
+    }
+
+    var leftTaskResizingHelper: AppResizingHelper? = null
+    var rightTaskResizingHelper: AppResizingHelper? = null
+    private var isTilingManagerInitialised = false
+    @VisibleForTesting
+    var desktopTilingDividerWindowManager: DesktopTilingDividerWindowManager? = null
+    private lateinit var dividerBounds: Rect
+    private var isResizing = false
+    private var isTilingFocused = false
+
+    fun onAppTiled(
+        taskInfo: RunningTaskInfo,
+        desktopModeWindowDecoration: DesktopModeWindowDecoration,
+        position: SnapPosition,
+        currentBounds: Rect,
+    ): Boolean {
+        val destinationBounds = getSnapBounds(taskInfo, position)
+        val resizeMetadata =
+            AppResizingHelper(
+                taskInfo,
+                desktopModeWindowDecoration,
+                context,
+                destinationBounds,
+                displayController,
+                transactionSupplier,
+            )
+        val isFirstTiledApp = leftTaskResizingHelper == null && rightTaskResizingHelper == null
+        val isTiled = destinationBounds != taskInfo.configuration.windowConfiguration.bounds
+
+        initTilingApps(resizeMetadata, position, taskInfo)
+        // Observe drag resizing to break tiling if a task is drag resized.
+        desktopModeWindowDecoration.addDragResizeListener(this)
+
+        if (isTiled) {
+            val wct = WindowContainerTransaction().setBounds(taskInfo.token, destinationBounds)
+            toggleResizeDesktopTaskTransitionHandler.startTransition(wct, currentBounds)
+        } else {
+            // Handle the case where we attempt to snap resize when already snap resized: the task
+            // position won't need to change but we want to animate the surface going back to the
+            // snapped position from the "dragged-to-the-edge" position.
+            if (destinationBounds != currentBounds) {
+                returnToDragStartAnimator.start(
+                    taskInfo.taskId,
+                    resizeMetadata.getLeash(),
+                    startBounds = currentBounds,
+                    endBounds = destinationBounds,
+                    isResizable = taskInfo.isResizeable,
+                )
+            }
+        }
+        initTilingForDisplayIfNeeded(taskInfo.configuration, isFirstTiledApp)
+        return isTiled
+    }
+
+    // If a task is already tiled on the same position, release this task, otherwise if the same
+    // task is tiled on the opposite side, remove it from the opposite side so it's tiled correctly.
+    private fun initTilingApps(
+        taskResizingHelper: AppResizingHelper,
+        position: SnapPosition,
+        taskInfo: RunningTaskInfo,
+    ) {
+        when (position) {
+            SnapPosition.RIGHT -> {
+                rightTaskResizingHelper?.let { removeTaskIfTiled(it.taskInfo.taskId) }
+                if (leftTaskResizingHelper?.taskInfo?.taskId == taskInfo.taskId) {
+                    removeTaskIfTiled(taskInfo.taskId)
+                }
+                rightTaskResizingHelper = taskResizingHelper
+            }
+
+            SnapPosition.LEFT -> {
+                leftTaskResizingHelper?.let { removeTaskIfTiled(it.taskInfo.taskId) }
+                if (taskInfo.taskId == rightTaskResizingHelper?.taskInfo?.taskId) {
+                    removeTaskIfTiled(taskInfo.taskId)
+                }
+                leftTaskResizingHelper = taskResizingHelper
+            }
+        }
+    }
+
+    private fun initTilingForDisplayIfNeeded(config: Configuration, firstTiledApp: Boolean) {
+        if (leftTaskResizingHelper != null && rightTaskResizingHelper != null) {
+            if (!isTilingManagerInitialised) {
+                desktopTilingDividerWindowManager = initTilingManagerForDisplay(displayId, config)
+                isTilingManagerInitialised = true
+                shellTaskOrganizer.addFocusListener(this)
+                isTilingFocused = true
+            }
+            leftTaskResizingHelper?.initIfNeeded()
+            rightTaskResizingHelper?.initIfNeeded()
+            leftTaskResizingHelper
+                ?.desktopModeWindowDecoration
+                ?.updateDisabledResizingEdge(
+                    DragResizeWindowGeometry.DisabledEdge.RIGHT,
+                    /* shouldDelayUpdate = */ false,
+                )
+            rightTaskResizingHelper
+                ?.desktopModeWindowDecoration
+                ?.updateDisabledResizingEdge(
+                    DragResizeWindowGeometry.DisabledEdge.LEFT,
+                    /* shouldDelayUpdate = */ false,
+                )
+        } else if (firstTiledApp) {
+            shellTaskOrganizer.addTaskVanishedListener(this)
+        }
+    }
+
+    private fun initTilingManagerForDisplay(
+        displayId: Int,
+        config: Configuration,
+    ): DesktopTilingDividerWindowManager? {
+        val displayLayout = displayController.getDisplayLayout(displayId)
+        val builder = SurfaceControl.Builder()
+        rootTdaOrganizer.attachToDisplayArea(displayId, builder)
+        val leash = builder.setName(TILING_DIVIDER_TAG).setContainerLayer().build()
+        val tilingManager =
+            displayLayout?.let {
+                dividerBounds = inflateDividerBounds(it)
+                DesktopTilingDividerWindowManager(
+                    config,
+                    TAG,
+                    context,
+                    leash,
+                    syncQueue,
+                    this,
+                    transactionSupplier,
+                    dividerBounds,
+                )
+            }
+        // a leash to present the divider on top of, without re-parenting.
+        val relativeLeash =
+            leftTaskResizingHelper?.desktopModeWindowDecoration?.getLeash() ?: return tilingManager
+        tilingManager?.generateViewHost(relativeLeash)
+        return tilingManager
+    }
+
+    fun onDividerHandleMoved(dividerBounds: Rect, t: SurfaceControl.Transaction): Boolean {
+        val leftTiledTask = leftTaskResizingHelper ?: return false
+        val rightTiledTask = rightTaskResizingHelper ?: return false
+        val stableBounds = Rect()
+        val displayLayout = displayController.getDisplayLayout(displayId)
+        displayLayout?.getStableBounds(stableBounds)
+
+        if (stableBounds.isEmpty) return false
+
+        val leftBounds = leftTiledTask.bounds
+        val rightBounds = rightTiledTask.bounds
+        val newLeftBounds =
+            Rect(leftBounds.left, leftBounds.top, dividerBounds.left, leftBounds.bottom)
+        val newRightBounds =
+            Rect(dividerBounds.right, rightBounds.top, rightBounds.right, rightBounds.bottom)
+
+        // If one of the apps is getting smaller or bigger than size constraint, ignore finger move.
+        if (
+            isResizeWithinSizeConstraints(
+                newLeftBounds,
+                newRightBounds,
+                leftBounds,
+                rightBounds,
+                stableBounds,
+            )
+        ) {
+            return false
+        }
+
+        // The final new bounds for each app has to be registered to make sure a startAnimate
+        // when the new bounds are different from old bounds, otherwise hide the veil without
+        // waiting for an animation as no animation will run when no bounds are changed.
+        leftTiledTask.newBounds.set(newLeftBounds)
+        rightTiledTask.newBounds.set(newRightBounds)
+        if (!isResizing) {
+            leftTiledTask.showVeil(t)
+            rightTiledTask.showVeil(t)
+            isResizing = true
+        } else {
+            leftTiledTask.updateVeil(t)
+            rightTiledTask.updateVeil(t)
+        }
+
+        // Applies showing/updating veil for both apps and moving the divider into its new position.
+        t.apply()
+        return true
+    }
+
+    fun onDividerHandleDragEnd(dividerBounds: Rect, t: SurfaceControl.Transaction) {
+        val leftTiledTask = leftTaskResizingHelper ?: return
+        val rightTiledTask = rightTaskResizingHelper ?: return
+
+        if (leftTiledTask.newBounds == leftTiledTask.bounds) {
+            leftTiledTask.hideVeil()
+            rightTiledTask.hideVeil()
+            isResizing = false
+            return
+        }
+        leftTiledTask.bounds.set(leftTiledTask.newBounds)
+        rightTiledTask.bounds.set(rightTiledTask.newBounds)
+        onDividerHandleMoved(dividerBounds, t)
+        isResizing = false
+        val wct = WindowContainerTransaction()
+        wct.setBounds(leftTiledTask.taskInfo.token, leftTiledTask.bounds)
+        wct.setBounds(rightTiledTask.taskInfo.token, rightTiledTask.bounds)
+        transitions.startTransition(TRANSIT_CHANGE, wct, this)
+    }
+
+    override fun startAnimation(
+        transition: IBinder,
+        info: TransitionInfo,
+        startTransaction: Transaction,
+        finishTransaction: Transaction,
+        finishCallback: Transitions.TransitionFinishCallback,
+    ): Boolean {
+        val leftTiledTask = leftTaskResizingHelper ?: return false
+        val rightTiledTask = rightTaskResizingHelper ?: return false
+        for (change in info.getChanges()) {
+            val sc: SurfaceControl = change.getLeash()
+            val endBounds =
+                if (change.taskInfo?.taskId == leftTiledTask.taskInfo.taskId) {
+                    leftTiledTask.bounds
+                } else {
+                    rightTiledTask.bounds
+                }
+            startTransaction.setWindowCrop(sc, endBounds.width(), endBounds.height())
+            finishTransaction.setWindowCrop(sc, endBounds.width(), endBounds.height())
+        }
+
+        startTransaction.apply()
+        leftTiledTask.hideVeil()
+        rightTiledTask.hideVeil()
+        finishCallback.onTransitionFinished(null)
+        return true
+    }
+
+    // TODO(b/361505243) bring tasks to front here when the empty request info bug is fixed.
+    override fun handleRequest(
+        transition: IBinder,
+        request: TransitionRequestInfo,
+    ): WindowContainerTransaction? {
+        return null
+    }
+
+    override fun onDragStart(taskId: Int) {}
+
+    override fun onDragMove(taskId: Int) {
+        removeTaskIfTiled(taskId)
+    }
+
+    override fun onTransitionReady(
+        transition: IBinder,
+        info: TransitionInfo,
+        startTransaction: Transaction,
+        finishTransaction: Transaction,
+    ) {
+        for (change in info.changes) {
+            change.taskInfo?.let {
+                if (it.isFullscreen || isMinimized(change.mode, info.type)) {
+                    removeTaskIfTiled(it.taskId, /* taskVanished= */ false, it.isFullscreen)
+                }
+            }
+        }
+    }
+
+    private fun isMinimized(changeMode: Int, infoType: Int): Boolean {
+        return (changeMode == TRANSIT_TO_BACK &&
+            (infoType == TRANSIT_MINIMIZE ||
+                infoType == TRANSIT_TO_BACK ||
+                infoType == TRANSIT_OPEN))
+    }
+
+    class AppResizingHelper(
+        val taskInfo: RunningTaskInfo,
+        val desktopModeWindowDecoration: DesktopModeWindowDecoration,
+        val context: Context,
+        val bounds: Rect,
+        val displayController: DisplayController,
+        val transactionSupplier: Supplier<Transaction>,
+    ) {
+        var isInitialised = false
+        var newBounds = Rect(bounds)
+        private lateinit var resizeVeilBitmap: Bitmap
+        private lateinit var resizeVeil: ResizeVeil
+        private val displayContext = displayController.getDisplayContext(taskInfo.displayId)
+
+        fun initIfNeeded() {
+            if (!isInitialised) {
+                initVeil()
+                isInitialised = true
+            }
+        }
+
+        private fun initVeil() {
+            val baseActivity = taskInfo.baseActivity
+            if (baseActivity == null) {
+                Slog.e(TAG, "Base activity component not found in task")
+                return
+            }
+            val resizeVeilIconFactory =
+                displayContext?.let {
+                    createIconFactory(displayContext, R.dimen.desktop_mode_resize_veil_icon_size)
+                } ?: return
+            val pm = context.getApplicationContext().getPackageManager()
+            val activityInfo = pm.getActivityInfo(baseActivity, 0 /* flags */)
+            val provider = IconProvider(displayContext)
+            val appIconDrawable = provider.getIcon(activityInfo)
+            resizeVeilBitmap =
+                resizeVeilIconFactory.createScaledBitmap(appIconDrawable, MODE_DEFAULT)
+            resizeVeil =
+                ResizeVeil(
+                    context = displayContext,
+                    displayController = displayController,
+                    appIcon = resizeVeilBitmap,
+                    parentSurface = desktopModeWindowDecoration.getLeash(),
+                    surfaceControlTransactionSupplier = transactionSupplier,
+                    taskInfo = taskInfo,
+                )
+        }
+
+        fun showVeil(t: Transaction) =
+            resizeVeil.updateTransactionWithShowVeil(
+                t,
+                desktopModeWindowDecoration.getLeash(),
+                bounds,
+                taskInfo,
+            )
+
+        fun updateVeil(t: Transaction) = resizeVeil.updateTransactionWithResizeVeil(t, newBounds)
+
+        fun hideVeil() = resizeVeil.hideVeil()
+
+        private fun createIconFactory(context: Context, dimensions: Int): BaseIconFactory {
+            val resources: Resources = context.resources
+            val densityDpi: Int = resources.getDisplayMetrics().densityDpi
+            val iconSize: Int = resources.getDimensionPixelSize(dimensions)
+            return BaseIconFactory(context, densityDpi, iconSize)
+        }
+
+        fun getLeash(): SurfaceControl = desktopModeWindowDecoration.getLeash()
+
+        fun dispose() {
+            if (isInitialised) resizeVeil.dispose()
+        }
+    }
+
+    private fun isTilingFocusRemoved(taskInfo: RunningTaskInfo): Boolean {
+        return taskInfo.isFocused &&
+            isTilingFocused &&
+            taskInfo.taskId != leftTaskResizingHelper?.taskInfo?.taskId &&
+            taskInfo.taskId != rightTaskResizingHelper?.taskInfo?.taskId
+    }
+
+    override fun onFocusTaskChanged(taskInfo: RunningTaskInfo?) {
+        if (taskInfo != null) {
+            moveTiledPairToFront(taskInfo)
+        }
+    }
+
+    private fun isTilingRefocused(taskInfo: RunningTaskInfo): Boolean {
+        return !isTilingFocused &&
+            taskInfo.isFocused &&
+            (taskInfo.taskId == leftTaskResizingHelper?.taskInfo?.taskId ||
+                taskInfo.taskId == rightTaskResizingHelper?.taskInfo?.taskId)
+    }
+
+    private fun buildTiledTasksMoveToFront(leftOnTop: Boolean): WindowContainerTransaction {
+        val wct = WindowContainerTransaction()
+        val leftTiledTask = leftTaskResizingHelper ?: return wct
+        val rightTiledTask = rightTaskResizingHelper ?: return wct
+        if (leftOnTop) {
+            wct.reorder(rightTiledTask.taskInfo.token, true)
+            wct.reorder(leftTiledTask.taskInfo.token, true)
+        } else {
+            wct.reorder(leftTiledTask.taskInfo.token, true)
+            wct.reorder(rightTiledTask.taskInfo.token, true)
+        }
+        return wct
+    }
+
+    fun removeTaskIfTiled(
+        taskId: Int,
+        taskVanished: Boolean = false,
+        shouldDelayUpdate: Boolean = false,
+    ) {
+        if (taskId == leftTaskResizingHelper?.taskInfo?.taskId) {
+            removeTask(leftTaskResizingHelper, taskVanished, shouldDelayUpdate)
+            leftTaskResizingHelper = null
+            rightTaskResizingHelper
+                ?.desktopModeWindowDecoration
+                ?.updateDisabledResizingEdge(NONE, shouldDelayUpdate)
+            tearDownTiling()
+            return
+        }
+
+        if (taskId == rightTaskResizingHelper?.taskInfo?.taskId) {
+            removeTask(rightTaskResizingHelper, taskVanished, shouldDelayUpdate)
+            rightTaskResizingHelper = null
+            leftTaskResizingHelper
+                ?.desktopModeWindowDecoration
+                ?.updateDisabledResizingEdge(NONE, shouldDelayUpdate)
+            tearDownTiling()
+        }
+    }
+
+    fun onUserChange() {
+        if (leftTaskResizingHelper != null) {
+            removeTask(leftTaskResizingHelper, taskVanished = false, shouldDelayUpdate = true)
+            leftTaskResizingHelper = null
+        }
+        if (rightTaskResizingHelper != null) {
+            removeTask(rightTaskResizingHelper, taskVanished = false, shouldDelayUpdate = true)
+            rightTaskResizingHelper = null
+        }
+        tearDownTiling()
+    }
+
+    private fun removeTask(
+        appResizingHelper: AppResizingHelper?,
+        taskVanished: Boolean = false,
+        shouldDelayUpdate: Boolean,
+    ) {
+        if (appResizingHelper == null) return
+        if (!taskVanished) {
+            appResizingHelper.desktopModeWindowDecoration.removeDragResizeListener(this)
+            appResizingHelper.desktopModeWindowDecoration.updateDisabledResizingEdge(
+                NONE,
+                shouldDelayUpdate,
+            )
+        }
+        appResizingHelper.dispose()
+    }
+
+    fun onOverviewAnimationStateChange(isRunning: Boolean) {
+        if (!isTilingManagerInitialised) return
+
+        if (isRunning) {
+            desktopTilingDividerWindowManager?.hideDividerBar()
+        } else if (allTiledTasksVisible()) {
+            desktopTilingDividerWindowManager?.showDividerBar()
+        }
+    }
+
+    override fun onTaskVanished(taskInfo: RunningTaskInfo?) {
+        val taskId = taskInfo?.taskId ?: return
+        removeTaskIfTiled(taskId, taskVanished = true, shouldDelayUpdate = true)
+    }
+
+    fun moveTiledPairToFront(taskInfo: RunningTaskInfo): Boolean {
+        if (!isTilingManagerInitialised) return false
+
+        // If a task that isn't tiled is being focused, let the generic handler do the work.
+        if (isTilingFocusRemoved(taskInfo)) {
+            isTilingFocused = false
+            return false
+        }
+
+        val leftTiledTask = leftTaskResizingHelper ?: return false
+        val rightTiledTask = rightTaskResizingHelper ?: return false
+        if (!allTiledTasksVisible()) return false
+        val isLeftOnTop = taskInfo.taskId == leftTiledTask.taskInfo.taskId
+        if (isTilingRefocused(taskInfo)) {
+            val t = transactionSupplier.get()
+            isTilingFocused = true
+            if (taskInfo.taskId == leftTaskResizingHelper?.taskInfo?.taskId) {
+                desktopTilingDividerWindowManager?.onRelativeLeashChanged(
+                    leftTiledTask.getLeash(),
+                    t,
+                )
+            }
+            if (taskInfo.taskId == rightTaskResizingHelper?.taskInfo?.taskId) {
+                desktopTilingDividerWindowManager?.onRelativeLeashChanged(
+                    rightTiledTask.getLeash(),
+                    t,
+                )
+            }
+            transitions.startTransition(
+                TRANSIT_TO_FRONT,
+                buildTiledTasksMoveToFront(isLeftOnTop),
+                null,
+            )
+            t.apply()
+            return true
+        }
+        return false
+    }
+
+    private fun allTiledTasksVisible(): Boolean {
+        val leftTiledTask = leftTaskResizingHelper ?: return false
+        val rightTiledTask = rightTaskResizingHelper ?: return false
+        return taskRepository.isVisibleTask(leftTiledTask.taskInfo.taskId) &&
+            taskRepository.isVisibleTask(rightTiledTask.taskInfo.taskId)
+    }
+
+    private fun isResizeWithinSizeConstraints(
+        newLeftBounds: Rect,
+        newRightBounds: Rect,
+        leftBounds: Rect,
+        rightBounds: Rect,
+        stableBounds: Rect,
+    ): Boolean {
+        return DragPositioningCallbackUtility.isExceedingWidthConstraint(
+            newLeftBounds.width(),
+            leftBounds.width(),
+            stableBounds,
+            displayController,
+            leftTaskResizingHelper?.desktopModeWindowDecoration,
+        ) ||
+            DragPositioningCallbackUtility.isExceedingWidthConstraint(
+                newRightBounds.width(),
+                rightBounds.width(),
+                stableBounds,
+                displayController,
+                rightTaskResizingHelper?.desktopModeWindowDecoration,
+            )
+    }
+
+    private fun getSnapBounds(taskInfo: RunningTaskInfo, position: SnapPosition): Rect {
+        val displayLayout = displayController.getDisplayLayout(taskInfo.displayId) ?: return Rect()
+
+        val stableBounds = Rect()
+        displayLayout.getStableBounds(stableBounds)
+        val leftTiledTask = leftTaskResizingHelper
+        val rightTiledTask = rightTaskResizingHelper
+        val destinationWidth = stableBounds.width() / 2
+        return when (position) {
+            SnapPosition.LEFT -> {
+                val rightBound =
+                    if (rightTiledTask == null) {
+                        stableBounds.left + destinationWidth -
+                            context.resources.getDimensionPixelSize(
+                                R.dimen.split_divider_bar_width
+                            ) / 2
+                    } else {
+                        rightTiledTask.bounds.left -
+                            context.resources.getDimensionPixelSize(R.dimen.split_divider_bar_width)
+                    }
+                Rect(stableBounds.left, stableBounds.top, rightBound, stableBounds.bottom)
+            }
+
+            SnapPosition.RIGHT -> {
+                val leftBound =
+                    if (leftTiledTask == null) {
+                        stableBounds.right - destinationWidth +
+                            context.resources.getDimensionPixelSize(
+                                R.dimen.split_divider_bar_width
+                            ) / 2
+                    } else {
+                        leftTiledTask.bounds.right +
+                            context.resources.getDimensionPixelSize(R.dimen.split_divider_bar_width)
+                    }
+                Rect(leftBound, stableBounds.top, stableBounds.right, stableBounds.bottom)
+            }
+        }
+    }
+
+    private fun inflateDividerBounds(displayLayout: DisplayLayout): Rect {
+        val stableBounds = Rect()
+        displayLayout.getStableBounds(stableBounds)
+
+        val leftDividerBounds = leftTaskResizingHelper?.bounds?.right ?: return Rect()
+        val rightDividerBounds = rightTaskResizingHelper?.bounds?.left ?: return Rect()
+
+        // Bounds should never be null here, so assertion is necessary otherwise it's illegal state.
+        return Rect(leftDividerBounds, stableBounds.top, rightDividerBounds, stableBounds.bottom)
+    }
+
+    private fun tearDownTiling() {
+        if (isTilingManagerInitialised) shellTaskOrganizer.removeFocusListener(this)
+
+        if (leftTaskResizingHelper == null && rightTaskResizingHelper == null) {
+            shellTaskOrganizer.removeTaskVanishedListener(this)
+        }
+        isTilingFocused = false
+        isTilingManagerInitialised = false
+        desktopTilingDividerWindowManager?.release()
+        desktopTilingDividerWindowManager = null
+    }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DividerMoveCallback.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DividerMoveCallback.kt
new file mode 100644
index 0000000..b3b30ad
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DividerMoveCallback.kt
@@ -0,0 +1,28 @@
+/*
+ * 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.windowdecor.tiling
+
+/** Divider move callback to whichever entity that handles the moving logic. */
+interface DividerMoveCallback {
+    /** Called on the divider move start gesture. */
+    fun onDividerMoveStart(pos: Int)
+
+    /** Called on the divider moved by dragging it. */
+    fun onDividerMove(pos: Int): Boolean
+
+    /** Called on divider move gesture end. */
+    fun onDividerMovedEnd(pos: Int)
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/TilingDividerView.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/TilingDividerView.kt
new file mode 100644
index 0000000..f8113c219
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/TilingDividerView.kt
@@ -0,0 +1,257 @@
+/*
+ * 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.windowdecor.tiling
+
+import android.content.Context
+import android.graphics.Canvas
+import android.graphics.Paint
+import android.graphics.Rect
+import android.provider.DeviceConfig
+import android.util.AttributeSet
+import android.view.MotionEvent
+import android.view.PointerIcon
+import android.view.View
+import android.view.ViewConfiguration
+import android.widget.FrameLayout
+import com.android.internal.annotations.VisibleForTesting
+import com.android.internal.config.sysui.SystemUiDeviceConfigFlags
+import com.android.wm.shell.R
+import com.android.wm.shell.common.split.DividerHandleView
+import com.android.wm.shell.common.split.DividerRoundedCorner
+import com.android.wm.shell.shared.animation.Interpolators
+import com.android.wm.shell.windowdecor.DragDetector
+
+/** Divider for tiling split screen, currently mostly a copy of [DividerView]. */
+class TilingDividerView : FrameLayout, View.OnTouchListener, DragDetector.MotionEventHandler {
+    private val paint = Paint()
+    private val backgroundRect = Rect()
+
+    private lateinit var callback: DividerMoveCallback
+    private lateinit var handle: DividerHandleView
+    private lateinit var corners: DividerRoundedCorner
+    private var touchElevation = 0
+
+    private var moving = false
+    private var startPos = 0
+    var handleRegionWidth: Int = 0
+    private var handleRegionHeight = 0
+    private var lastAcceptedPos = 0
+    @VisibleForTesting var handleStartY = 0
+    @VisibleForTesting var handleEndY = 0
+    private var canResize = false
+    private var resized = false
+    /**
+     * Tracks divider bar visible bounds in screen-based coordination. Used to calculate with
+     * insets.
+     */
+    private val dividerBounds = Rect()
+    private var dividerBar: FrameLayout? = null
+    private lateinit var dragDetector: DragDetector
+
+    constructor(context: Context) : super(context)
+
+    constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
+
+    constructor(
+        context: Context,
+        attrs: AttributeSet?,
+        defStyleAttr: Int,
+    ) : super(context, attrs, defStyleAttr)
+
+    constructor(
+        context: Context,
+        attrs: AttributeSet?,
+        defStyleAttr: Int,
+        defStyleRes: Int,
+    ) : super(context, attrs, defStyleAttr, defStyleRes)
+
+    /** Sets up essential dependencies of the divider bar. */
+    fun setup(dividerMoveCallback: DividerMoveCallback, dividerBounds: Rect) {
+        callback = dividerMoveCallback
+        this.dividerBounds.set(dividerBounds)
+        handle.setIsLeftRightSplit(true)
+        corners.setIsLeftRightSplit(true)
+        handleRegionHeight =
+            resources.getDimensionPixelSize(R.dimen.split_divider_handle_region_width)
+
+        handleRegionWidth =
+            resources.getDimensionPixelSize(R.dimen.split_divider_handle_region_height)
+        initHandleYCoordinates()
+        dragDetector =
+            DragDetector(
+                this,
+                /* holdToDragMinDurationMs= */ 0,
+                ViewConfiguration.get(mContext).scaledTouchSlop,
+            )
+    }
+
+    override fun onFinishInflate() {
+        super.onFinishInflate()
+        dividerBar = requireViewById(R.id.divider_bar)
+        handle = requireViewById(R.id.docked_divider_handle)
+        corners = requireViewById(R.id.docked_divider_rounded_corner)
+        touchElevation =
+            resources.getDimensionPixelSize(R.dimen.docked_stack_divider_lift_elevation)
+        setOnTouchListener(this)
+        setWillNotDraw(false)
+        paint.color = resources.getColor(R.color.split_divider_background, null)
+        paint.isAntiAlias = true
+        paint.style = Paint.Style.FILL
+    }
+
+    override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
+        super.onLayout(changed, left, top, right, bottom)
+        if (changed) {
+            val dividerSize = resources.getDimensionPixelSize(R.dimen.split_divider_bar_width)
+            val backgroundLeft = (width - dividerSize) / 2
+            val backgroundTop = 0
+            val backgroundRight = left + dividerSize
+            val backgroundBottom = height
+            backgroundRect.set(backgroundLeft, backgroundTop, backgroundRight, backgroundBottom)
+        }
+    }
+
+    override fun onResolvePointerIcon(event: MotionEvent, pointerIndex: Int): PointerIcon =
+        PointerIcon.getSystemIcon(context, PointerIcon.TYPE_HORIZONTAL_DOUBLE_ARROW)
+
+    override fun onTouch(v: View, event: MotionEvent): Boolean =
+        dragDetector.onMotionEvent(v, event)
+
+    private fun setTouching() {
+        handle.setTouching(true, true)
+        // Lift handle as well so it doesn't get behind the background, even though it doesn't
+        // cast shadow.
+        handle
+            .animate()
+            .setInterpolator(Interpolators.TOUCH_RESPONSE)
+            .setDuration(TOUCH_ANIMATION_DURATION)
+            .translationZ(touchElevation.toFloat())
+            .start()
+    }
+
+    private fun releaseTouching() {
+        handle.setTouching(false, true)
+        handle
+            .animate()
+            .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
+            .setDuration(TOUCH_RELEASE_ANIMATION_DURATION)
+            .translationZ(0f)
+            .start()
+    }
+
+    override fun onHoverEvent(event: MotionEvent): Boolean {
+        if (
+            !DeviceConfig.getBoolean(
+                DeviceConfig.NAMESPACE_SYSTEMUI,
+                SystemUiDeviceConfigFlags.CURSOR_HOVER_STATES_ENABLED,
+                /* defaultValue = */ false,
+            )
+        ) {
+            return false
+        }
+
+        if (event.action == MotionEvent.ACTION_HOVER_ENTER) {
+            setHovering()
+            return true
+        }
+        if (event.action == MotionEvent.ACTION_HOVER_EXIT) {
+            releaseHovering()
+            return true
+        }
+        return false
+    }
+
+    @VisibleForTesting
+    fun setHovering() {
+        handle.setHovering(true, true)
+        handle
+            .animate()
+            .setInterpolator(Interpolators.TOUCH_RESPONSE)
+            .setDuration(TOUCH_ANIMATION_DURATION)
+            .translationZ(touchElevation.toFloat())
+            .start()
+    }
+
+    override fun onDraw(canvas: Canvas) {
+        canvas.drawRect(backgroundRect, paint)
+    }
+
+    @VisibleForTesting
+    fun releaseHovering() {
+        handle.setHovering(false, true)
+        handle
+            .animate()
+            .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
+            .setDuration(TOUCH_RELEASE_ANIMATION_DURATION)
+            .translationZ(0f)
+            .start()
+    }
+
+    override fun handleMotionEvent(v: View?, event: MotionEvent): Boolean {
+        val touchPos = event.rawX.toInt()
+        val yTouchPosInDivider = event.y.toInt()
+        when (event.actionMasked) {
+            MotionEvent.ACTION_DOWN -> {
+                if (!isWithinHandleRegion(yTouchPosInDivider)) return true
+                callback.onDividerMoveStart(touchPos)
+                setTouching()
+                canResize = true
+            }
+
+            MotionEvent.ACTION_MOVE -> {
+                if (!canResize) return true
+                if (!moving) {
+                    startPos = touchPos
+                    moving = true
+                }
+
+                val pos = dividerBounds.left + touchPos - startPos
+                if (callback.onDividerMove(pos)) {
+                    lastAcceptedPos = touchPos
+                    resized = true
+                }
+            }
+
+            MotionEvent.ACTION_CANCEL,
+            MotionEvent.ACTION_UP -> {
+                if (!canResize) return true
+                if (moving && resized) {
+                    dividerBounds.left = dividerBounds.left + lastAcceptedPos - startPos
+                    callback.onDividerMovedEnd(dividerBounds.left)
+                }
+                moving = false
+                canResize = false
+                resized = false
+                releaseTouching()
+            }
+        }
+        return true
+    }
+
+    private fun isWithinHandleRegion(touchYPos: Int): Boolean {
+        return touchYPos in handleStartY..handleEndY
+    }
+
+    private fun initHandleYCoordinates() {
+        handleStartY = (dividerBounds.height() - handleRegionHeight) / 2
+        handleEndY = handleStartY + handleRegionHeight
+    }
+
+    companion object {
+        const val TOUCH_ANIMATION_DURATION: Long = 150
+        const val TOUCH_RELEASE_ANIMATION_DURATION: Long = 200
+    }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolder.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolder.kt
index b5700ff..c4e4946 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolder.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolder.kt
@@ -36,13 +36,10 @@
 import android.widget.ImageButton
 import androidx.core.view.ViewCompat
 import androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat
-import com.android.internal.policy.SystemBarUtils
-import com.android.window.flags.Flags
 import com.android.wm.shell.R
 import com.android.wm.shell.shared.animation.Interpolators
 import com.android.wm.shell.windowdecor.WindowManagerWrapper
 import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalSystemViewContainer
-import com.android.wm.shell.windowdecor.viewholder.WindowDecorationViewHolder.Data
 
 /**
  * A desktop mode window decoration used when the window is in full "focus" (i.e. fullscreen/split).
@@ -69,10 +66,12 @@
     ) : Data()
 
     private lateinit var taskInfo: RunningTaskInfo
+    private val position: Point = Point()
+    private var width: Int = 0
+    private var height: Int = 0
     private val captionView: View = rootView.requireViewById(R.id.desktop_mode_caption)
     private val captionHandle: ImageButton = rootView.requireViewById(R.id.caption_handle)
     private val inputManager = context.getSystemService(InputManager::class.java)
-    private var statusBarInputLayerExists = false
 
     // An invisible View that takes up the same coordinates as captionHandle but is layered
     // above the status bar. The purpose of this View is to receive input intended for
@@ -112,21 +111,54 @@
     ) {
         captionHandle.imageTintList = ColorStateList.valueOf(getCaptionHandleBarColor(taskInfo))
         this.taskInfo = taskInfo
-        // If handle is not in status bar region(i.e., bottom stage in vertical split),
-        // do not create an input layer
-        if (position.y >= SystemBarUtils.getStatusBarHeight(context)) return
-        if (!isCaptionVisible && statusBarInputLayerExists) {
-            disposeStatusBarInputLayer()
+        this.position.set(position)
+        this.width = width
+        this.height = height
+        if (!isCaptionVisible && statusBarInputLayer != null) {
+            detachStatusBarInputLayer()
             return
         }
-        // Input layer view creation / modification takes a significant amount of time;
+    }
+
+    fun bindStatusBarInputLayer(
+        statusBarLayer: AdditionalSystemViewContainer
+    ) {
+        // Input layer view modification takes a significant amount of time;
         // post them so we don't hold up DesktopModeWindowDecoration#relayout.
-        if (statusBarInputLayerExists) {
+        if (statusBarLayer == statusBarInputLayer) {
             handler.post { updateStatusBarInputLayer(position) }
-        } else {
-            // Input layer is created on a delay; prevent multiple from being created.
-            statusBarInputLayerExists = true
-            handler.post { createStatusBarInputLayer(position, width, height) }
+            return
+        }
+        // Remove the old input layer when changing to a new one.
+        if (statusBarInputLayer != null) detachStatusBarInputLayer()
+        if (statusBarLayer.view.visibility == View.GONE) {
+            statusBarLayer.view.visibility = View.VISIBLE
+        }
+        statusBarInputLayer = statusBarLayer
+        statusBarInputLayer?.let {
+            inputLayer -> setupAppHandleA11y(inputLayer.view)
+        }
+        handler.post {
+            val view = statusBarInputLayer?.view
+                ?: error("Unable to find statusBarInputLayer View")
+            // Caption handle is located within the status bar region, meaning the
+            // DisplayPolicy will attempt to transfer this input to status bar if it's
+            // a swipe down. Pilfer here to keep the gesture in handle alone.
+            view.setOnTouchListener { v, event ->
+                if (event.actionMasked == ACTION_DOWN) {
+                    inputManager.pilferPointers(v.viewRootImpl.inputToken)
+                }
+                captionHandle.dispatchTouchEvent(event)
+                return@setOnTouchListener true
+            }
+            view.setOnHoverListener { _, event ->
+                captionHandle.onHoverEvent(event)
+            }
+            val lp = statusBarInputLayer?.view?.layoutParams as WindowManager.LayoutParams
+            lp.x = position.x
+            lp.y = position.y
+            lp.width = width
+            lp.height = height
         }
     }
 
@@ -138,40 +170,6 @@
         animateCaptionHandleAlpha(startValue = 0f, endValue = 1f)
     }
 
-    private fun createStatusBarInputLayer(handlePosition: Point,
-                                          handleWidth: Int,
-                                          handleHeight: Int) {
-        if (!Flags.enableHandleInputFix()) return
-        statusBarInputLayer = AdditionalSystemViewContainer(context, windowManagerWrapper,
-            taskInfo.taskId, handlePosition.x, handlePosition.y, handleWidth, handleHeight,
-            WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
-        )
-        val view = statusBarInputLayer?.view ?: error("Unable to find statusBarInputLayer View")
-        val lp = statusBarInputLayer?.lp ?: error("Unable to find statusBarInputLayer " +
-                "LayoutParams")
-        lp.title = "Handle Input Layer of task " + taskInfo.taskId
-        lp.setTrustedOverlay()
-        // Make this window a spy window to enable it to pilfer pointers from the system-wide
-        // gesture listener that receives events before window. This is to prevent notification
-        // shade gesture when we swipe down to enter desktop.
-        lp.inputFeatures = WindowManager.LayoutParams.INPUT_FEATURE_SPY
-        view.setOnHoverListener { _, event ->
-            captionHandle.onHoverEvent(event)
-        }
-        // Caption handle is located within the status bar region, meaning the
-        // DisplayPolicy will attempt to transfer this input to status bar if it's
-        // a swipe down. Pilfer here to keep the gesture in handle alone.
-        view.setOnTouchListener { v, event ->
-            if (event.actionMasked == ACTION_DOWN) {
-                inputManager.pilferPointers(v.viewRootImpl.inputToken)
-            }
-            captionHandle.dispatchTouchEvent(event)
-            return@setOnTouchListener true
-        }
-        setupAppHandleA11y(view)
-        windowManagerWrapper.updateViewLayout(view, lp)
-    }
-
     private fun setupAppHandleA11y(view: View) {
         view.accessibilityDelegate = object : View.AccessibilityDelegate() {
             override fun onInitializeAccessibilityNodeInfo(
@@ -224,15 +222,12 @@
     }
 
     /**
-     * Remove the input layer from [WindowManager]. Should be used when caption handle
-     * is not visible.
+     * Remove the input listeners from the input layer and remove it from this view holder.
      */
-    fun disposeStatusBarInputLayer() {
-        statusBarInputLayerExists = false
-        handler.post {
-            statusBarInputLayer?.releaseView()
-            statusBarInputLayer = null
-        }
+    fun detachStatusBarInputLayer() {
+        statusBarInputLayer?.view?.setOnTouchListener(null)
+        statusBarInputLayer?.view?.setOnHoverListener(null)
+        statusBarInputLayer = null
     }
 
     private fun getCaptionHandleBarColor(taskInfo: RunningTaskInfo): Int {
diff --git a/libs/WindowManager/Shell/tests/unittest/Android.bp b/libs/WindowManager/Shell/tests/unittest/Android.bp
index 049a5a0..91be5f5 100644
--- a/libs/WindowManager/Shell/tests/unittest/Android.bp
+++ b/libs/WindowManager/Shell/tests/unittest/Android.bp
@@ -63,6 +63,7 @@
         "com.android.window.flags.window-aconfig-java",
         "platform-test-annotations",
         "flag-junit",
+        "platform-parametric-runner-lib",
     ],
 
     libs: [
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java
index f01ed84..841b6ce 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java
@@ -45,6 +45,8 @@
 import android.app.TaskInfo;
 import android.content.LocusId;
 import android.content.pm.ParceledListSlice;
+import android.graphics.Point;
+import android.graphics.Rect;
 import android.os.Binder;
 import android.os.IBinder;
 import android.os.RemoteException;
@@ -589,9 +591,9 @@
 
     @Test
     public void testRecentTasks_visibilityChanges_shouldNotifyTaskController() {
-        RunningTaskInfo task1 = createTaskInfo(/* taskId= */ 1, WINDOWING_MODE_FREEFORM);
+        RunningTaskInfo task1 = createFreeformTaskInfo(/* taskId= */ 1);
         mOrganizer.onTaskAppeared(task1, /* leash= */ null);
-        RunningTaskInfo task2 = createTaskInfo(/* taskId= */ 1, WINDOWING_MODE_FREEFORM);
+        RunningTaskInfo task2 = createFreeformTaskInfo(/* taskId= */ 1);
         task2.isVisible = false;
 
         mOrganizer.onTaskInfoChanged(task2);
@@ -600,6 +602,30 @@
     }
 
     @Test
+    public void testRecentTasks_sizeChanges_shouldNotifyTaskController() {
+        RunningTaskInfo task1 = createFreeformTaskInfo(/* taskId= */ 1);
+        mOrganizer.onTaskAppeared(task1, /* leash= */ null);
+        RunningTaskInfo task2 = createFreeformTaskInfo(/* taskId= */ 1);
+        task2.configuration.windowConfiguration.setAppBounds(new Rect(0, 0, 300, 400));
+
+        mOrganizer.onTaskInfoChanged(task2);
+
+        verify(mRecentTasksController).onTaskRunningInfoChanged(task2);
+    }
+
+    @Test
+    public void testRecentTasks_positionChanges_shouldNotifyTaskController() {
+        RunningTaskInfo task1 = createFreeformTaskInfo(/* taskId= */ 1);
+        mOrganizer.onTaskAppeared(task1, /* leash= */ null);
+        RunningTaskInfo task2 = createFreeformTaskInfo(/* taskId= */ 1);
+        task2.positionInParent = new Point(200, 200);
+
+        mOrganizer.onTaskInfoChanged(task2);
+
+        verify(mRecentTasksController).onTaskRunningInfoChanged(task2);
+    }
+
+    @Test
     public void testRecentTasks_visibilityChanges_notFreeForm_shouldNotNotifyTaskController() {
         RunningTaskInfo task1_visible = createTaskInfo(/* taskId= */ 1, WINDOWING_MODE_FULLSCREEN);
         mOrganizer.onTaskAppeared(task1_visible, /* leash= */ null);
@@ -649,6 +675,13 @@
         return taskInfo;
     }
 
+    private static RunningTaskInfo createFreeformTaskInfo(int taskId) {
+        RunningTaskInfo taskInfo = createTaskInfo(taskId, WINDOWING_MODE_FREEFORM);
+        taskInfo.positionInParent = new Point(100, 100);
+        taskInfo.configuration.windowConfiguration.setAppBounds(new Rect(0, 0, 200, 200));
+        return taskInfo;
+    }
+
     private void verifyOnCompatInfoChangedInvokedWith(TaskInfo taskInfo,
                                                       ShellTaskOrganizer.TaskListener listener) {
         final ArgumentCaptor<CompatUIInfo> capture = ArgumentCaptor.forClass(CompatUIInfo.class);
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 6b62adb..72d4dc6 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
@@ -51,6 +51,7 @@
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.hardware.input.InputManager;
+import android.os.Binder;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
@@ -812,7 +813,10 @@
         if (taskId != INVALID_TASK_ID) {
             final ActivityManager.RunningTaskInfo taskInfo = new ActivityManager.RunningTaskInfo();
             taskInfo.taskId = taskId;
-            taskInfo.token = new WindowContainerToken(mock(IWindowContainerToken.class));
+            final IWindowContainerToken mockT = mock(IWindowContainerToken.class);
+            Binder binder = new Binder();
+            doReturn(binder).when(mockT).asBinder();
+            taskInfo.token = new WindowContainerToken(mockT);
             change = new TransitionInfo.Change(
                     taskInfo.token, b.build());
             change.setTaskInfo(taskInfo);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleViewInfoTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleViewInfoTest.kt
index 4ac066e..1f2eaa6 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleViewInfoTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleViewInfoTest.kt
@@ -99,10 +99,11 @@
         val shellController = ShellController(context, shellInit, shellCommandHandler,
 					      mock<DisplayInsetsController>(), mainExecutor)
         bubblePositioner = BubblePositioner(context, windowManager)
+        val bubbleLogger = mock<BubbleLogger>()
         val bubbleData =
             BubbleData(
                 context,
-                mock<BubbleLogger>(),
+                bubbleLogger,
                 bubblePositioner,
                 BubbleEducationController(context),
                 mainExecutor,
@@ -125,7 +126,7 @@
                 WindowManagerShellWrapper(mainExecutor),
                 mock<UserManager>(),
                 mock<LauncherApps>(),
-                mock<BubbleLogger>(),
+                bubbleLogger,
                 mock<TaskStackListenerImpl>(),
                 ShellTaskOrganizer(mainExecutor),
                 bubblePositioner,
@@ -154,7 +155,7 @@
                 bubbleController,
                 mainExecutor
             )
-        bubbleBarLayerView = BubbleBarLayerView(context, bubbleController, bubbleData)
+        bubbleBarLayerView = BubbleBarLayerView(context, bubbleController, bubbleData, bubbleLogger)
     }
 
     @Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java
index c52d9dd..dc0f213 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java
@@ -190,7 +190,7 @@
     }
 
     private void waitDividerFlingFinished() {
-        verify(mSplitLayout).flingDividerPosition(anyInt(), anyInt(), anyInt(),
+        verify(mSplitLayout).flingDividerPosition(anyInt(), anyInt(), anyInt(), any(),
                 mRunnableCaptor.capture());
         mRunnableCaptor.getValue().run();
     }
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopActivityOrientationChangeHandlerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopActivityOrientationChangeHandlerTest.kt
index 3e9c732..aabd973 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopActivityOrientationChangeHandlerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopActivityOrientationChangeHandlerTest.kt
@@ -43,6 +43,7 @@
 import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createFullscreenTask
 import com.android.wm.shell.desktopmode.persistence.Desktop
 import com.android.wm.shell.desktopmode.persistence.DesktopPersistentRepository
+import com.android.wm.shell.desktopmode.persistence.DesktopRepositoryInitializer
 import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
 import com.android.wm.shell.sysui.ShellInit
 import com.android.wm.shell.transition.Transitions
@@ -50,10 +51,10 @@
 import junit.framework.Assert.assertTrue
 import kotlin.test.assertNotNull
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.cancel
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.SupervisorJob
+import kotlinx.coroutines.cancel
 import kotlinx.coroutines.runBlocking
 import kotlinx.coroutines.test.StandardTestDispatcher
 import kotlinx.coroutines.test.setMain
@@ -94,6 +95,7 @@
     @Mock lateinit var resizeTransitionHandler: ToggleResizeDesktopTaskTransitionHandler
     @Mock lateinit var taskStackListener: TaskStackListenerImpl
     @Mock lateinit var persistentRepository: DesktopPersistentRepository
+    @Mock lateinit var repositoryInitializer: DesktopRepositoryInitializer
 
     private lateinit var mockitoSession: StaticMockitoSession
     private lateinit var handler: DesktopActivityOrientationChangeHandler
@@ -116,7 +118,13 @@
         testScope = CoroutineScope(Dispatchers.Unconfined + SupervisorJob())
         shellInit = spy(ShellInit(testExecutor))
         taskRepository =
-            DesktopRepository(context, shellInit, persistentRepository, testScope)
+            DesktopRepository(
+                context,
+                shellInit,
+                persistentRepository,
+                repositoryInitializer,
+                testScope
+            )
         whenever(shellTaskOrganizer.getRunningTasks(anyInt())).thenAnswer { runningTasks }
         whenever(transitions.startTransition(anyInt(), any(), isNull())).thenAnswer { Binder() }
         whenever(runBlocking { persistentRepository.readDesktop(any(), any()) }).thenReturn(
@@ -172,8 +180,7 @@
         activityInfo.screenOrientation = SCREEN_ORIENTATION_PORTRAIT
         task.topActivityInfo = activityInfo
         whenever(shellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(task)
-        taskRepository.addActiveTask(DEFAULT_DISPLAY, task.taskId)
-        taskRepository.updateTaskVisibility(DEFAULT_DISPLAY, task.taskId, visible = true)
+        taskRepository.addTask(DEFAULT_DISPLAY, task.taskId, isVisible = true)
         runningTasks.add(task)
 
         taskStackListener.onActivityRequestedOrientationChanged(task.taskId,
@@ -196,7 +203,7 @@
     @Test
     fun handleActivityOrientationChange_notInDesktopMode_doNothing() {
         val task = setUpFreeformTask(isResizeable = false)
-        taskRepository.updateTaskVisibility(task.displayId, task.taskId, visible = false)
+        taskRepository.updateTask(task.displayId, task.taskId, isVisible = false)
 
         taskStackListener.onActivityRequestedOrientationChanged(task.taskId,
             SCREEN_ORIENTATION_LANDSCAPE)
@@ -261,9 +268,7 @@
         task.topActivityInfo = activityInfo
         task.isResizeable = isResizeable
         whenever(shellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(task)
-        taskRepository.addActiveTask(displayId, task.taskId)
-        taskRepository.updateTaskVisibility(displayId, task.taskId, visible = true)
-        taskRepository.addOrMoveFreeformTaskToTop(displayId, task.taskId)
+        taskRepository.addTask(displayId, task.taskId, isVisible = true)
         runningTasks.add(task)
         return task
     }
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopImmersiveControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopImmersiveControllerTest.kt
index e83f5c7..e05a0b5 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopImmersiveControllerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopImmersiveControllerTest.kt
@@ -81,7 +81,7 @@
     @Before
     fun setUp() {
         desktopRepository = DesktopRepository(
-            context, ShellInit(TestShellExecutor()), mock(), mock()
+            context, ShellInit(TestShellExecutor()), mock(), mock(), mock()
         )
         whenever(mockDisplayController.getDisplayLayout(DEFAULT_DISPLAY))
             .thenReturn(mockDisplayLayout)
@@ -347,7 +347,7 @@
             wct = wct,
             displayId = DEFAULT_DISPLAY,
             excludeTaskId = task.taskId
-        )?.invoke(transition)
+        ).asExit()?.runOnTransitionStart?.invoke(transition)
 
         assertThat(controller.pendingExternalExitTransitions.any { exit ->
             exit.transition == transition && exit.displayId == DEFAULT_DISPLAY
@@ -402,7 +402,8 @@
             immersive = true
         )
 
-        controller.exitImmersiveIfApplicable(wct, task)?.invoke(transition)
+        controller.exitImmersiveIfApplicable(wct, task)
+            .asExit()?.runOnTransitionStart?.invoke(transition)
 
         assertThat(controller.pendingExternalExitTransitions.any { exit ->
             exit.transition == transition && exit.displayId == DEFAULT_DISPLAY
@@ -412,23 +413,36 @@
 
     @Test
     @EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP)
-    fun exitImmersiveIfApplicable_byTask_notInImmersive_doesNotAddPendingExitOnRun() {
+    fun exitImmersiveIfApplicable_byTask_notInImmersive_doesNotExit() {
         val task = createFreeformTask()
         whenever(mockShellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(task)
         val wct = WindowContainerTransaction()
-        val transition = Binder()
         desktopRepository.setTaskInFullImmersiveState(
-            displayId = DEFAULT_DISPLAY,
+            displayId = task.displayId,
             taskId = task.taskId,
             immersive = false
         )
 
-        controller.exitImmersiveIfApplicable(wct, task)?.invoke(transition)
+        val result = controller.exitImmersiveIfApplicable(wct, task)
 
-        assertThat(controller.pendingExternalExitTransitions.any { exit ->
-            exit.transition == transition && exit.displayId == DEFAULT_DISPLAY
-                    && exit.taskId == task.taskId
-        }).isFalse()
+        assertThat(result).isEqualTo(DesktopImmersiveController.ExitResult.NoExit)
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP)
+    fun exitImmersiveIfApplicable_byDisplay_notInImmersive_doesNotExit() {
+        val task = createFreeformTask()
+        whenever(mockShellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(task)
+        val wct = WindowContainerTransaction()
+        desktopRepository.setTaskInFullImmersiveState(
+            displayId = task.displayId,
+            taskId = task.taskId,
+            immersive = false
+        )
+
+        val result = controller.exitImmersiveIfApplicable(wct, task.displayId)
+
+        assertThat(result).isEqualTo(DesktopImmersiveController.ExitResult.NoExit)
     }
 
     @Test
@@ -631,7 +645,8 @@
             taskId = task.taskId,
             immersive = true
         )
-        controller.exitImmersiveIfApplicable(wct, task)?.invoke(Binder())
+        controller.exitImmersiveIfApplicable(wct, task)
+            .asExit()?.runOnTransitionStart?.invoke(Binder())
 
         controller.moveTaskToNonImmersive(task)
 
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopMixedTransitionHandlerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopMixedTransitionHandlerTest.kt
index be0663c..868883d 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopMixedTransitionHandlerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopMixedTransitionHandlerTest.kt
@@ -31,6 +31,8 @@
 import android.testing.TestableLooper.RunWithLooper
 import android.view.SurfaceControl
 import android.view.WindowManager
+import android.view.WindowManager.TRANSIT_OPEN
+import android.view.WindowManager.TransitionType
 import android.window.TransitionInfo
 import android.window.WindowContainerTransaction
 import androidx.test.filters.SmallTest
@@ -41,6 +43,7 @@
 import com.android.wm.shell.TestRunningTaskInfoBuilder
 import com.android.wm.shell.freeform.FreeformTaskTransitionHandler
 import com.android.wm.shell.transition.Transitions
+import com.google.common.truth.Truth.assertThat
 import org.junit.Assert.assertFalse
 import org.junit.Assert.assertNull
 import org.junit.Assert.assertTrue
@@ -51,6 +54,8 @@
 import org.mockito.Mock
 import org.mockito.Mockito.verify
 import org.mockito.kotlin.any
+import org.mockito.kotlin.anyOrNull
+import org.mockito.kotlin.argThat
 import org.mockito.kotlin.eq
 import org.mockito.kotlin.mock
 import org.mockito.kotlin.whenever
@@ -71,6 +76,7 @@
     @Mock lateinit var desktopRepository: DesktopRepository
     @Mock lateinit var freeformTaskTransitionHandler: FreeformTaskTransitionHandler
     @Mock lateinit var closeDesktopTaskTransitionHandler: CloseDesktopTaskTransitionHandler
+    @Mock lateinit var desktopImmersiveController: DesktopImmersiveController
     @Mock lateinit var interactionJankMonitor: InteractionJankMonitor
     @Mock lateinit var mockHandler: Handler
     @Mock lateinit var closingTaskLeash: SurfaceControl
@@ -86,6 +92,7 @@
                 desktopRepository,
                 freeformTaskTransitionHandler,
                 closeDesktopTaskTransitionHandler,
+                desktopImmersiveController,
                 interactionJankMonitor,
                 mockHandler
             )
@@ -146,7 +153,7 @@
         val transition = mock<IBinder>()
         val transitionInfo =
             createTransitionInfo(
-                changeMode = WindowManager.TRANSIT_OPEN,
+                changeMode = TRANSIT_OPEN,
                 task = createTask(WINDOWING_MODE_FREEFORM)
             )
         whenever(freeformTaskTransitionHandler.startAnimation(any(), any(), any(), any(), any()))
@@ -164,7 +171,9 @@
     }
 
     @Test
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_EXIT_TRANSITIONS)
     fun startAnimation_withClosingDesktopTask_callsCloseTaskHandler() {
+        val wct = WindowContainerTransaction()
         val transition = mock<IBinder>()
         val transitionInfo = createTransitionInfo(task = createTask(WINDOWING_MODE_FREEFORM))
         whenever(desktopRepository.getExpandedTaskCount(any())).thenReturn(2)
@@ -172,6 +181,9 @@
                 closeDesktopTaskTransitionHandler.startAnimation(any(), any(), any(), any(), any())
             )
             .thenReturn(true)
+        whenever(transitions.startTransition(WindowManager.TRANSIT_CLOSE, wct, mixedHandler))
+            .thenReturn(transition)
+        mixedHandler.startRemoveTransition(wct)
 
         val started = mixedHandler.startAnimation(
             transition = transition,
@@ -187,12 +199,17 @@
     }
 
     @Test
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_EXIT_TRANSITIONS)
     fun startAnimation_withClosingLastDesktopTask_dispatchesTransition() {
+        val wct = WindowContainerTransaction()
         val transition = mock<IBinder>()
         val transitionInfo = createTransitionInfo(task = createTask(WINDOWING_MODE_FREEFORM))
         whenever(desktopRepository.getExpandedTaskCount(any())).thenReturn(1)
         whenever(transitions.dispatchTransition(any(), any(), any(), any(), any(), any()))
             .thenReturn(mock())
+        whenever(transitions.startTransition(WindowManager.TRANSIT_CLOSE, wct, mixedHandler))
+            .thenReturn(transition)
+        mixedHandler.startRemoveTransition(wct)
 
         mixedHandler.startAnimation(
             transition = transition,
@@ -220,6 +237,176 @@
             )
     }
 
+    @Test
+    @DisableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP)
+    fun startLaunchTransition_immersiveMixDisabled_doesNotUseMixedHandler() {
+        val wct = WindowContainerTransaction()
+        val task = createTask(WINDOWING_MODE_FREEFORM)
+        whenever(transitions.startTransition(eq(TRANSIT_OPEN), eq(wct), anyOrNull()))
+            .thenReturn(Binder())
+
+        mixedHandler.startLaunchTransition(
+            transitionType = TRANSIT_OPEN,
+            wct = wct,
+            taskId = task.taskId,
+            exitingImmersiveTask = null
+        )
+
+        verify(transitions).startTransition(TRANSIT_OPEN, wct, /* handler= */ null)
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP)
+    fun startLaunchTransition_immersiveMixEnabled_usesMixedHandler() {
+        val wct = WindowContainerTransaction()
+        val task = createTask(WINDOWING_MODE_FREEFORM)
+        whenever(transitions.startTransition(eq(TRANSIT_OPEN), eq(wct), anyOrNull()))
+            .thenReturn(Binder())
+
+        mixedHandler.startLaunchTransition(
+            transitionType = TRANSIT_OPEN,
+            wct = wct,
+            taskId = task.taskId,
+            exitingImmersiveTask = null
+        )
+
+        verify(transitions).startTransition(TRANSIT_OPEN, wct, mixedHandler)
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP)
+    fun startAndAnimateLaunchTransition_withoutImmersiveChange_dispatchesAllChangesToLeftOver() {
+        val wct = WindowContainerTransaction()
+        val launchingTask = createTask(WINDOWING_MODE_FREEFORM)
+        val transition = Binder()
+        whenever(transitions.startTransition(eq(TRANSIT_OPEN), eq(wct), anyOrNull()))
+            .thenReturn(transition)
+
+        mixedHandler.startLaunchTransition(
+            transitionType = TRANSIT_OPEN,
+            wct = wct,
+            taskId = launchingTask.taskId,
+            exitingImmersiveTask = null,
+        )
+        val launchTaskChange = createChange(launchingTask)
+        val otherChange = createChange(createTask(WINDOWING_MODE_FREEFORM))
+        mixedHandler.startAnimation(
+            transition,
+            createTransitionInfo(
+                TRANSIT_OPEN,
+                listOf(launchTaskChange, otherChange)
+            ),
+            SurfaceControl.Transaction(),
+            SurfaceControl.Transaction(),
+        ) { }
+
+        verify(transitions).dispatchTransition(
+            eq(transition),
+            argThat { info ->
+                info.changes.contains(launchTaskChange) && info.changes.contains(otherChange)
+            },
+            any(),
+            any(),
+            any(),
+            eq(mixedHandler),
+        )
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP)
+    fun startAndAnimateLaunchTransition_withImmersiveChange_mixesAnimations() {
+        val wct = WindowContainerTransaction()
+        val launchingTask = createTask(WINDOWING_MODE_FREEFORM)
+        val immersiveTask = createTask(WINDOWING_MODE_FREEFORM)
+        val transition = Binder()
+        whenever(transitions.startTransition(eq(TRANSIT_OPEN), eq(wct), anyOrNull()))
+            .thenReturn(transition)
+
+        mixedHandler.startLaunchTransition(
+            transitionType = TRANSIT_OPEN,
+            wct = wct,
+            taskId = launchingTask.taskId,
+            exitingImmersiveTask = immersiveTask.taskId,
+        )
+        val launchTaskChange = createChange(launchingTask)
+        val immersiveChange = createChange(immersiveTask)
+        mixedHandler.startAnimation(
+            transition,
+            createTransitionInfo(
+                TRANSIT_OPEN,
+                listOf(launchTaskChange, immersiveChange)
+            ),
+            SurfaceControl.Transaction(),
+            SurfaceControl.Transaction(),
+        ) { }
+
+        verify(desktopImmersiveController)
+            .animateResizeChange(eq(immersiveChange), any(), any(), any())
+        verify(transitions).dispatchTransition(
+            eq(transition),
+            argThat { info ->
+                info.changes.contains(launchTaskChange) && !info.changes.contains(immersiveChange)
+            },
+            any(),
+            any(),
+            any(),
+            eq(mixedHandler),
+        )
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP)
+    fun startAndAnimateLaunchTransition_removesPendingMixedTransition() {
+        val wct = WindowContainerTransaction()
+        val launchingTask = createTask(WINDOWING_MODE_FREEFORM)
+        val transition = Binder()
+        whenever(transitions.startTransition(eq(TRANSIT_OPEN), eq(wct), anyOrNull()))
+            .thenReturn(transition)
+
+        mixedHandler.startLaunchTransition(
+            transitionType = TRANSIT_OPEN,
+            wct = wct,
+            taskId = launchingTask.taskId,
+            exitingImmersiveTask = null,
+        )
+        val launchTaskChange = createChange(launchingTask)
+        mixedHandler.startAnimation(
+            transition,
+            createTransitionInfo(
+                TRANSIT_OPEN,
+                listOf(launchTaskChange)
+            ),
+            SurfaceControl.Transaction(),
+            SurfaceControl.Transaction(),
+        ) { }
+
+        assertThat(mixedHandler.pendingMixedTransitions).isEmpty()
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP)
+    fun startAndAnimateLaunchTransition_aborted_removesPendingMixedTransition() {
+        val wct = WindowContainerTransaction()
+        val launchingTask = createTask(WINDOWING_MODE_FREEFORM)
+        val transition = Binder()
+        whenever(transitions.startTransition(eq(TRANSIT_OPEN), eq(wct), anyOrNull()))
+            .thenReturn(transition)
+
+        mixedHandler.startLaunchTransition(
+            transitionType = TRANSIT_OPEN,
+            wct = wct,
+            taskId = launchingTask.taskId,
+            exitingImmersiveTask = null,
+        )
+        mixedHandler.onTransitionConsumed(
+            transition = transition,
+            aborted = true,
+            finishTransaction = SurfaceControl.Transaction()
+        )
+
+        assertThat(mixedHandler.pendingMixedTransitions).isEmpty()
+    }
+
     private fun createTransitionInfo(
         type: Int = WindowManager.TRANSIT_CLOSE,
         changeMode: Int = WindowManager.TRANSIT_CLOSE,
@@ -235,6 +422,18 @@
             )
         }
 
+    private fun createTransitionInfo(
+        @TransitionType type: Int,
+        changes: List<TransitionInfo.Change> = emptyList()
+    ): TransitionInfo = TransitionInfo(type, /* flags= */ 0).apply {
+        changes.forEach { change -> addChange(change) }
+    }
+
+    private fun createChange(task: RunningTaskInfo): TransitionInfo.Change =
+        TransitionInfo.Change(task.token, SurfaceControl()).apply {
+            taskInfo = task
+        }
+
     private fun createTask(@WindowingMode windowingMode: Int): RunningTaskInfo =
         TestRunningTaskInfoBuilder()
             .setActivityType(ACTIVITY_TYPE_STANDARD)
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 f25faa5..7c4ce4a 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
@@ -59,6 +59,7 @@
 import com.android.wm.shell.sysui.ShellInit
 import com.android.wm.shell.transition.TransitionInfoBuilder
 import com.android.wm.shell.transition.Transitions
+import com.android.wm.shell.transition.Transitions.TRANSIT_MINIMIZE
 import kotlin.test.assertFalse
 import kotlin.test.assertTrue
 import org.junit.Before
@@ -522,6 +523,23 @@
   }
 
   @Test
+  fun transitMinimize_logExitReasongMinimized() {
+      // add a freeform task
+      transitionObserver.addTaskInfosToCachedMap(createTaskInfo(WINDOWING_MODE_FREEFORM))
+      transitionObserver.isSessionActive = true
+
+      // minimize the task
+      val change = createChange(TRANSIT_MINIMIZE, createTaskInfo(WINDOWING_MODE_FULLSCREEN))
+      val transitionInfo = TransitionInfoBuilder(TRANSIT_MINIMIZE).addChange(change).build()
+      callOnTransitionReady(transitionInfo)
+
+      assertFalse(transitionObserver.isSessionActive)
+      verify(desktopModeEventLogger, times(1)).logSessionExit(eq(ExitReason.TASK_MINIMIZED))
+      verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(DEFAULT_TASK_UPDATE))
+      verifyZeroInteractions(desktopModeEventLogger)
+  }
+
+  @Test
   fun sessionExitByRecents_cancelledAnimation_sessionRestored() {
     // add a freeform task to an existing session
     val taskInfo = createTaskInfo(WINDOWING_MODE_FREEFORM)
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopRepositoryTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopRepositoryTest.kt
index d90443c..38d9ab9 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopRepositoryTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopRepositoryTest.kt
@@ -18,6 +18,7 @@
 
 import android.graphics.Rect
 import android.platform.test.annotations.EnableFlags
+import android.platform.test.flag.junit.SetFlagsRule
 import android.testing.AndroidTestingRunner
 import android.util.ArraySet
 import android.view.Display.DEFAULT_DISPLAY
@@ -29,6 +30,7 @@
 import com.android.wm.shell.common.ShellExecutor
 import com.android.wm.shell.desktopmode.persistence.Desktop
 import com.android.wm.shell.desktopmode.persistence.DesktopPersistentRepository
+import com.android.wm.shell.desktopmode.persistence.DesktopRepositoryInitializer
 import com.android.wm.shell.sysui.ShellInit
 import com.google.common.truth.Truth.assertThat
 import junit.framework.Assert.fail
@@ -43,6 +45,7 @@
 import kotlinx.coroutines.test.setMain
 import org.junit.After
 import org.junit.Before
+import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.Mock
@@ -58,12 +61,15 @@
 @ExperimentalCoroutinesApi
 class DesktopRepositoryTest : ShellTestCase() {
 
+    @JvmField @Rule val setFlagsRule = SetFlagsRule()
+
     private lateinit var repo: DesktopRepository
     private lateinit var shellInit: ShellInit
     private lateinit var datastoreScope: CoroutineScope
 
     @Mock private lateinit var testExecutor: ShellExecutor
     @Mock private lateinit var persistentRepository: DesktopPersistentRepository
+    @Mock lateinit var repositoryInitializer: DesktopRepositoryInitializer
 
     @Before
     fun setUp() {
@@ -71,7 +77,14 @@
         datastoreScope = CoroutineScope(Dispatchers.Unconfined + SupervisorJob())
         shellInit = spy(ShellInit(testExecutor))
 
-        repo = DesktopRepository(context, shellInit, persistentRepository, datastoreScope)
+        repo =
+            DesktopRepository(
+                context,
+                shellInit,
+                persistentRepository,
+                repositoryInitializer,
+                datastoreScope
+            )
         whenever(runBlocking { persistentRepository.readDesktop(any(), any()) }).thenReturn(
             Desktop.getDefaultInstance()
         )
@@ -84,65 +97,65 @@
     }
 
     @Test
-    fun addActiveTask_notifiesListener() {
+    fun addTask_notifiesActiveTaskListener() {
         val listener = TestListener()
         repo.addActiveTaskListener(listener)
 
-        repo.addActiveTask(DEFAULT_DISPLAY, taskId = 1)
+        repo.addTask(DEFAULT_DISPLAY, taskId = 1, isVisible = true)
 
         assertThat(listener.activeChangesOnDefaultDisplay).isEqualTo(1)
     }
 
     @Test
-    fun addActiveTask_taskIsActive() {
+    fun addTask_marksTaskActive() {
         val listener = TestListener()
         repo.addActiveTaskListener(listener)
 
-        repo.addActiveTask(DEFAULT_DISPLAY, taskId = 1)
+        repo.addTask(DEFAULT_DISPLAY, taskId = 1, isVisible = true)
 
         assertThat(repo.isActiveTask(1)).isTrue()
     }
 
     @Test
-    fun addSameActiveTaskTwice_notifiesOnce() {
+    fun addSameTaskTwice_notifiesOnce() {
         val listener = TestListener()
         repo.addActiveTaskListener(listener)
 
-        repo.addActiveTask(DEFAULT_DISPLAY, taskId = 1)
-        repo.addActiveTask(DEFAULT_DISPLAY, taskId = 1)
+        repo.addTask(DEFAULT_DISPLAY, taskId = 1, isVisible = true)
+        repo.addTask(DEFAULT_DISPLAY, taskId = 1, isVisible = true)
 
         assertThat(listener.activeChangesOnDefaultDisplay).isEqualTo(1)
     }
 
     @Test
-    fun addActiveTask_multipleTasksAdded_notifiesForAllTasks() {
+    fun addTask_multipleTasksAdded_notifiesForAllTasks() {
         val listener = TestListener()
         repo.addActiveTaskListener(listener)
 
-        repo.addActiveTask(DEFAULT_DISPLAY, taskId = 1)
-        repo.addActiveTask(DEFAULT_DISPLAY, taskId = 2)
+        repo.addTask(DEFAULT_DISPLAY, taskId = 1, isVisible = true)
+        repo.addTask(DEFAULT_DISPLAY, taskId = 2, isVisible = true)
 
         assertThat(listener.activeChangesOnDefaultDisplay).isEqualTo(2)
     }
 
     @Test
-    fun addActiveTask_multipleDisplays_notifiesCorrectListener() {
+    fun addTask_multipleDisplays_notifiesCorrectListener() {
         val listener = TestListener()
         repo.addActiveTaskListener(listener)
 
-        repo.addActiveTask(DEFAULT_DISPLAY, taskId = 1)
-        repo.addActiveTask(DEFAULT_DISPLAY, taskId = 2)
-        repo.addActiveTask(SECOND_DISPLAY, taskId = 3)
+        repo.addTask(DEFAULT_DISPLAY, taskId = 1, isVisible = true)
+        repo.addTask(DEFAULT_DISPLAY, taskId = 2, isVisible = true)
+        repo.addTask(SECOND_DISPLAY, taskId = 3, isVisible = true)
 
         assertThat(listener.activeChangesOnDefaultDisplay).isEqualTo(2)
         assertThat(listener.activeChangesOnSecondaryDisplay).isEqualTo(1)
     }
 
     @Test
-    fun removeActiveTask_notifiesListener() {
+    fun removeActiveTask_notifiesActiveTaskListener() {
         val listener = TestListener()
         repo.addActiveTaskListener(listener)
-        repo.addActiveTask(DEFAULT_DISPLAY, taskId = 1)
+        repo.addTask(DEFAULT_DISPLAY, taskId = 1, isVisible = true)
 
         repo.removeActiveTask(1)
 
@@ -151,10 +164,10 @@
     }
 
     @Test
-    fun removeActiveTask_taskNotActive() {
+    fun removeActiveTask_marksTaskNotActive() {
         val listener = TestListener()
         repo.addActiveTaskListener(listener)
-        repo.addActiveTask(DEFAULT_DISPLAY, taskId = 1)
+        repo.addTask(DEFAULT_DISPLAY, taskId = 1, isVisible = true)
 
         repo.removeActiveTask(1)
 
@@ -175,7 +188,7 @@
     fun remoteActiveTask_listenerForOtherDisplayNotNotified() {
         val listener = TestListener()
         repo.addActiveTaskListener(listener)
-        repo.addActiveTask(DEFAULT_DISPLAY, taskId = 1)
+        repo.addTask(DEFAULT_DISPLAY, taskId = 1, isVisible = true)
 
         repo.removeActiveTask(1)
 
@@ -201,8 +214,8 @@
     }
 
     @Test
-    fun updateTaskVisibility_singleVisibleNonClosingTask_updatesTasksCorrectly() {
-        repo.updateTaskVisibility(DEFAULT_DISPLAY, taskId = 1, visible = true)
+    fun updateTask_singleVisibleNonClosingTask_updatesTasksCorrectly() {
+        repo.updateTask(DEFAULT_DISPLAY, taskId = 1, isVisible = true)
 
         assertThat(repo.isVisibleTask(1)).isTrue()
         assertThat(repo.isClosingTask(1)).isFalse()
@@ -215,7 +228,7 @@
 
     @Test
     fun isOnlyVisibleNonClosingTask_singleVisibleClosingTask() {
-        repo.updateTaskVisibility(DEFAULT_DISPLAY, taskId = 1, visible = true)
+        repo.updateTask(DEFAULT_DISPLAY, taskId = 1, isVisible = true)
         repo.addClosingTask(DEFAULT_DISPLAY, 1)
 
         // A visible task that's closing
@@ -229,13 +242,14 @@
 
     @Test
     fun isOnlyVisibleNonClosingTask_singleVisibleMinimizedTask() {
-        repo.updateTaskVisibility(DEFAULT_DISPLAY, taskId = 1, visible = true)
-        repo.minimizeTask(DEFAULT_DISPLAY, 1)
+        val taskId = 1
+        repo.addTask(DEFAULT_DISPLAY, taskId, isVisible = true)
+        repo.minimizeTask(DEFAULT_DISPLAY, taskId)
 
         // The visible task that's closing
-        assertThat(repo.isVisibleTask(1)).isTrue()
-        assertThat(repo.isMinimizedTask(1)).isTrue()
-        assertThat(repo.isOnlyVisibleNonClosingTask(1)).isFalse()
+        assertThat(repo.isVisibleTask(taskId)).isFalse()
+        assertThat(repo.isMinimizedTask(taskId)).isTrue()
+        assertThat(repo.isOnlyVisibleNonClosingTask(taskId)).isFalse()
         // Not a visible task
         assertThat(repo.isVisibleTask(99)).isFalse()
         assertThat(repo.isOnlyVisibleNonClosingTask(99)).isFalse()
@@ -243,17 +257,19 @@
 
     @Test
     fun isOnlyVisibleNonClosingTask_multipleVisibleNonClosingTasks() {
-        repo.updateTaskVisibility(DEFAULT_DISPLAY, taskId = 1, visible = true)
-        repo.updateTaskVisibility(DEFAULT_DISPLAY, taskId = 2, visible = true)
+        repo.updateTask(DEFAULT_DISPLAY, taskId = 1, isVisible = true)
+        repo.updateTask(DEFAULT_DISPLAY, taskId = 2, isVisible = true)
 
         // Not the only task
         assertThat(repo.isVisibleTask(1)).isTrue()
         assertThat(repo.isClosingTask(1)).isFalse()
         assertThat(repo.isOnlyVisibleNonClosingTask(1)).isFalse()
+
         // Not the only task
         assertThat(repo.isVisibleTask(2)).isTrue()
         assertThat(repo.isClosingTask(2)).isFalse()
         assertThat(repo.isOnlyVisibleNonClosingTask(2)).isFalse()
+
         // Not a visible task
         assertThat(repo.isVisibleTask(99)).isFalse()
         assertThat(repo.isClosingTask(99)).isFalse()
@@ -262,9 +278,9 @@
 
     @Test
     fun isOnlyVisibleNonClosingTask_multipleDisplays() {
-        repo.updateTaskVisibility(DEFAULT_DISPLAY, taskId = 1, visible = true)
-        repo.updateTaskVisibility(DEFAULT_DISPLAY, taskId = 2, visible = true)
-        repo.updateTaskVisibility(SECOND_DISPLAY, taskId = 3, visible = true)
+        repo.updateTask(DEFAULT_DISPLAY, taskId = 1, isVisible = true)
+        repo.updateTask(DEFAULT_DISPLAY, taskId = 2, isVisible = true)
+        repo.updateTask(SECOND_DISPLAY, taskId = 3, isVisible = true)
 
         // Not the only task on DEFAULT_DISPLAY
         assertThat(repo.isVisibleTask(1)).isTrue()
@@ -282,7 +298,7 @@
 
     @Test
     fun addVisibleTasksListener_notifiesVisibleFreeformTask() {
-        repo.updateTaskVisibility(DEFAULT_DISPLAY, taskId = 1, visible = true)
+        repo.updateTask(DEFAULT_DISPLAY, taskId = 1, isVisible = true)
         val listener = TestVisibilityListener()
         val executor = TestShellExecutor()
 
@@ -295,7 +311,7 @@
 
     @Test
     fun addListener_tasksOnDifferentDisplay_doesNotNotify() {
-        repo.updateTaskVisibility(SECOND_DISPLAY, taskId = 1, visible = true)
+        repo.updateTask(SECOND_DISPLAY, taskId = 1, isVisible = true)
         val listener = TestVisibilityListener()
         val executor = TestShellExecutor()
         repo.addVisibleTasksListener(listener, executor)
@@ -307,13 +323,13 @@
     }
 
     @Test
-    fun updateTaskVisibility_addVisibleTasksNotifiesListener() {
+    fun updateTask_visible_addVisibleTasksNotifiesListener() {
         val listener = TestVisibilityListener()
         val executor = TestShellExecutor()
         repo.addVisibleTasksListener(listener, executor)
 
-        repo.updateTaskVisibility(DEFAULT_DISPLAY, taskId = 1, visible = true)
-        repo.updateTaskVisibility(DEFAULT_DISPLAY, taskId = 2, visible = true)
+        repo.updateTask(DEFAULT_DISPLAY, taskId = 1, isVisible = true)
+        repo.updateTask(DEFAULT_DISPLAY, taskId = 2, isVisible = true)
         executor.flushAll()
 
         assertThat(listener.visibleTasksCountOnDefaultDisplay).isEqualTo(2)
@@ -321,12 +337,12 @@
     }
 
     @Test
-    fun updateTaskVisibility_addVisibleTaskNotifiesListenerForThatDisplay() {
+    fun updateTask_visibleTask_addVisibleTaskNotifiesListenerForThatDisplay() {
         val listener = TestVisibilityListener()
         val executor = TestShellExecutor()
         repo.addVisibleTasksListener(listener, executor)
 
-        repo.updateTaskVisibility(DEFAULT_DISPLAY, taskId = 1, visible = true)
+        repo.updateTask(DEFAULT_DISPLAY, taskId = 1, isVisible = true)
         executor.flushAll()
 
         assertThat(listener.visibleTasksCountOnDefaultDisplay).isEqualTo(1)
@@ -334,7 +350,7 @@
         assertThat(listener.visibleTasksCountOnSecondaryDisplay).isEqualTo(0)
         assertThat(listener.visibleChangesOnSecondaryDisplay).isEqualTo(0)
 
-        repo.updateTaskVisibility(displayId = 1, taskId = 2, visible = true)
+        repo.updateTask(displayId = 1, taskId = 2, isVisible = true)
         executor.flushAll()
 
         // Listener for secondary display is notified
@@ -345,17 +361,17 @@
     }
 
     @Test
-    fun updateTaskVisibility_taskOnDefaultBecomesVisibleOnSecondDisplay_listenersNotified() {
+    fun updateTask_taskOnDefaultBecomesVisibleOnSecondDisplay_listenersNotified() {
         val listener = TestVisibilityListener()
         val executor = TestShellExecutor()
         repo.addVisibleTasksListener(listener, executor)
 
-        repo.updateTaskVisibility(DEFAULT_DISPLAY, taskId = 1, visible = true)
+        repo.updateTask(DEFAULT_DISPLAY, taskId = 1, isVisible = true)
         executor.flushAll()
         assertThat(listener.visibleTasksCountOnDefaultDisplay).isEqualTo(1)
 
         // Mark task 1 visible on secondary display
-        repo.updateTaskVisibility(displayId = 1, taskId = 1, visible = true)
+        repo.updateTask(displayId = 1, taskId = 1, isVisible = true)
         executor.flushAll()
 
         // Default display should have 2 calls
@@ -370,22 +386,22 @@
     }
 
     @Test
-    fun updateTaskVisibility_removeVisibleTasksNotifiesListener() {
+    fun updateTask_removeVisibleTasksNotifiesListener() {
         val listener = TestVisibilityListener()
         val executor = TestShellExecutor()
         repo.addVisibleTasksListener(listener, executor)
-        repo.updateTaskVisibility(DEFAULT_DISPLAY, taskId = 1, visible = true)
-        repo.updateTaskVisibility(DEFAULT_DISPLAY, taskId = 2, visible = true)
+        repo.updateTask(DEFAULT_DISPLAY, taskId = 1, isVisible = true)
+        repo.updateTask(DEFAULT_DISPLAY, taskId = 2, isVisible = true)
         executor.flushAll()
 
         assertThat(listener.visibleTasksCountOnDefaultDisplay).isEqualTo(2)
 
-        repo.updateTaskVisibility(DEFAULT_DISPLAY, taskId = 1, visible = false)
+        repo.updateTask(DEFAULT_DISPLAY, taskId = 1, isVisible = false)
         executor.flushAll()
 
         assertThat(listener.visibleChangesOnDefaultDisplay).isEqualTo(3)
 
-        repo.updateTaskVisibility(DEFAULT_DISPLAY, taskId = 2, visible = false)
+        repo.updateTask(DEFAULT_DISPLAY, taskId = 2, isVisible = false)
         executor.flushAll()
 
         assertThat(listener.visibleTasksCountOnDefaultDisplay).isEqualTo(0)
@@ -397,17 +413,17 @@
      * This tests that task is removed from the last parent display when it vanishes.
      */
     @Test
-    fun updateTaskVisibility_removeVisibleTasksRemovesTaskWithInvalidDisplay() {
+    fun updateTask_removeVisibleTasksRemovesTaskWithInvalidDisplay() {
         val listener = TestVisibilityListener()
         val executor = TestShellExecutor()
         repo.addVisibleTasksListener(listener, executor)
-        repo.updateTaskVisibility(DEFAULT_DISPLAY, taskId = 1, visible = true)
-        repo.updateTaskVisibility(DEFAULT_DISPLAY, taskId = 2, visible = true)
+        repo.updateTask(DEFAULT_DISPLAY, taskId = 1, isVisible = true)
+        repo.updateTask(DEFAULT_DISPLAY, taskId = 2, isVisible = true)
         executor.flushAll()
 
         assertThat(listener.visibleTasksCountOnDefaultDisplay).isEqualTo(2)
 
-        repo.updateTaskVisibility(INVALID_DISPLAY, taskId = 1, visible = false)
+        repo.updateTask(INVALID_DISPLAY, taskId = 1, isVisible = false)
         executor.flushAll()
 
         assertThat(listener.visibleChangesOnDefaultDisplay).isEqualTo(3)
@@ -420,30 +436,30 @@
         assertThat(repo.getVisibleTaskCount(DEFAULT_DISPLAY)).isEqualTo(0)
 
         // New task increments count to 1
-        repo.updateTaskVisibility(DEFAULT_DISPLAY, taskId = 1, visible = true)
+        repo.updateTask(DEFAULT_DISPLAY, taskId = 1, isVisible = true)
 
         assertThat(repo.getVisibleTaskCount(DEFAULT_DISPLAY)).isEqualTo(1)
 
         // Visibility update to same task does not increase count
-        repo.updateTaskVisibility(DEFAULT_DISPLAY, taskId = 1, visible = true)
+        repo.updateTask(DEFAULT_DISPLAY, taskId = 1, isVisible = true)
 
         assertThat(repo.getVisibleTaskCount(DEFAULT_DISPLAY)).isEqualTo(1)
 
         // Second task visible increments count
-        repo.updateTaskVisibility(DEFAULT_DISPLAY, taskId = 2, visible = true)
+        repo.updateTask(DEFAULT_DISPLAY, taskId = 2, isVisible = true)
 
         assertThat(repo.getVisibleTaskCount(DEFAULT_DISPLAY)).isEqualTo(2)
 
         // Hiding a task decrements count
-        repo.updateTaskVisibility(DEFAULT_DISPLAY, taskId = 1, visible = false)
+        repo.updateTask(DEFAULT_DISPLAY, taskId = 1, isVisible = false)
         assertThat(repo.getVisibleTaskCount(DEFAULT_DISPLAY)).isEqualTo(1)
 
         // Hiding all tasks leaves count at 0
-        repo.updateTaskVisibility(DEFAULT_DISPLAY, taskId = 2, visible = false)
+        repo.updateTask(DEFAULT_DISPLAY, taskId = 2, isVisible = false)
         assertThat(repo.getVisibleTaskCount(displayId = 9)).isEqualTo(0)
 
         // Hiding a not existing task, count remains at 0
-        repo.updateTaskVisibility(DEFAULT_DISPLAY, taskId = 999, visible = false)
+        repo.updateTask(DEFAULT_DISPLAY, taskId = 999, isVisible = false)
         assertThat(repo.getVisibleTaskCount(DEFAULT_DISPLAY)).isEqualTo(0)
     }
 
@@ -453,42 +469,42 @@
         assertThat(repo.getVisibleTaskCount(SECOND_DISPLAY)).isEqualTo(0)
 
         // New task on default display increments count for that display only
-        repo.updateTaskVisibility(DEFAULT_DISPLAY, taskId = 1, visible = true)
+        repo.updateTask(DEFAULT_DISPLAY, taskId = 1, isVisible = true)
 
         assertThat(repo.getVisibleTaskCount(DEFAULT_DISPLAY)).isEqualTo(1)
         assertThat(repo.getVisibleTaskCount(SECOND_DISPLAY)).isEqualTo(0)
 
         // New task on secondary display, increments count for that display only
-        repo.updateTaskVisibility(SECOND_DISPLAY, taskId = 2, visible = true)
+        repo.updateTask(SECOND_DISPLAY, taskId = 2, isVisible = true)
 
         assertThat(repo.getVisibleTaskCount(DEFAULT_DISPLAY)).isEqualTo(1)
         assertThat(repo.getVisibleTaskCount(SECOND_DISPLAY)).isEqualTo(1)
 
         // Marking task visible on another display, updates counts for both displays
-        repo.updateTaskVisibility(SECOND_DISPLAY, taskId = 1, visible = true)
+        repo.updateTask(SECOND_DISPLAY, taskId = 1, isVisible = true)
 
         assertThat(repo.getVisibleTaskCount(DEFAULT_DISPLAY)).isEqualTo(0)
         assertThat(repo.getVisibleTaskCount(SECOND_DISPLAY)).isEqualTo(2)
 
         // Marking task that is on secondary display, hidden on default display, does not affect
         // secondary display
-        repo.updateTaskVisibility(DEFAULT_DISPLAY, taskId = 1, visible = false)
+        repo.updateTask(DEFAULT_DISPLAY, taskId = 1, isVisible = false)
 
         assertThat(repo.getVisibleTaskCount(DEFAULT_DISPLAY)).isEqualTo(0)
         assertThat(repo.getVisibleTaskCount(SECOND_DISPLAY)).isEqualTo(2)
 
         // Hiding a task on that display, decrements count
-        repo.updateTaskVisibility(SECOND_DISPLAY, taskId = 1, visible = false)
+        repo.updateTask(SECOND_DISPLAY, taskId = 1, isVisible = false)
 
         assertThat(repo.getVisibleTaskCount(DEFAULT_DISPLAY)).isEqualTo(0)
         assertThat(repo.getVisibleTaskCount(SECOND_DISPLAY)).isEqualTo(1)
     }
 
     @Test
-    fun addOrMoveFreeformTaskToTop_didNotExist_addsToTop() {
-        repo.addOrMoveFreeformTaskToTop(DEFAULT_DISPLAY, 5)
-        repo.addOrMoveFreeformTaskToTop(DEFAULT_DISPLAY, 6)
-        repo.addOrMoveFreeformTaskToTop(DEFAULT_DISPLAY, 7)
+    fun addTask_didNotExist_addsToTop() {
+        repo.addTask(DEFAULT_DISPLAY, 5, isVisible = true)
+        repo.addTask(DEFAULT_DISPLAY, 6, isVisible = true)
+        repo.addTask(DEFAULT_DISPLAY, 7, isVisible = true)
 
         val tasks = repo.getFreeformTasksInZOrder(DEFAULT_DISPLAY)
         assertThat(tasks.size).isEqualTo(3)
@@ -499,11 +515,11 @@
 
     @Test
     @EnableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_PERSISTENCE)
-    fun addOrMoveFreeformTaskToTop_noTaskExists_persistenceEnabled_addsToTop() =
+    fun addTask_noTaskExists_persistenceEnabled_addsToTop() =
         runTest(StandardTestDispatcher()) {
-            repo.addOrMoveFreeformTaskToTop(DEFAULT_DISPLAY, 5)
-            repo.addOrMoveFreeformTaskToTop(DEFAULT_DISPLAY, 6)
-            repo.addOrMoveFreeformTaskToTop(DEFAULT_DISPLAY, 7)
+            repo.addTask(DEFAULT_DISPLAY, 5, isVisible = true)
+            repo.addTask(DEFAULT_DISPLAY, 6, isVisible = true)
+            repo.addTask(DEFAULT_DISPLAY, 7, isVisible = true)
 
             val tasks = repo.getFreeformTasksInZOrder(DEFAULT_DISPLAY)
             assertThat(tasks).containsExactly(7, 6, 5).inOrder()
@@ -520,7 +536,7 @@
                     .addOrUpdateDesktop(
                         DEFAULT_USER_ID,
                         DEFAULT_DESKTOP_ID,
-                        visibleTasks = ArraySet(),
+                        visibleTasks = ArraySet(arrayOf(5)),
                         minimizedTasks = ArraySet(),
                         freeformTasksInZOrder = arrayListOf(6, 5)
                     )
@@ -528,7 +544,7 @@
                     .addOrUpdateDesktop(
                         DEFAULT_USER_ID,
                         DEFAULT_DESKTOP_ID,
-                        visibleTasks = ArraySet(),
+                        visibleTasks = ArraySet(arrayOf(5, 6)),
                         minimizedTasks = ArraySet(),
                         freeformTasksInZOrder = arrayListOf(7, 6, 5)
                     )
@@ -536,12 +552,12 @@
     }
 
     @Test
-    fun addOrMoveFreeformTaskToTop_alreadyExists_movesToTop() {
-        repo.addOrMoveFreeformTaskToTop(DEFAULT_DISPLAY, 5)
-        repo.addOrMoveFreeformTaskToTop(DEFAULT_DISPLAY, 6)
-        repo.addOrMoveFreeformTaskToTop(DEFAULT_DISPLAY, 7)
+    fun addTask_alreadyExists_movesToTop() {
+        repo.addTask(DEFAULT_DISPLAY, 5, isVisible = true)
+        repo.addTask(DEFAULT_DISPLAY, 6, isVisible = true)
+        repo.addTask(DEFAULT_DISPLAY, 7, isVisible = true)
 
-        repo.addOrMoveFreeformTaskToTop(DEFAULT_DISPLAY, 6)
+        repo.addTask(DEFAULT_DISPLAY, 6, isVisible = true)
 
         val tasks = repo.getFreeformTasksInZOrder(DEFAULT_DISPLAY)
         assertThat(tasks.size).isEqualTo(3)
@@ -549,10 +565,10 @@
     }
 
     @Test
-    fun addOrMoveFreeformTaskToTop_taskIsMinimized_unminimizesTask() {
-        repo.addOrMoveFreeformTaskToTop(DEFAULT_DISPLAY, 5)
-        repo.addOrMoveFreeformTaskToTop(DEFAULT_DISPLAY, 6)
-        repo.addOrMoveFreeformTaskToTop(DEFAULT_DISPLAY, 7)
+    fun addTask_taskIsMinimized_unminimizesTask() {
+        repo.addTask(DEFAULT_DISPLAY, 5, isVisible = true)
+        repo.addTask(DEFAULT_DISPLAY, 6, isVisible = true)
+        repo.addTask(DEFAULT_DISPLAY, 7, isVisible = true)
         repo.minimizeTask(displayId = 0, taskId = 6)
 
         val tasks = repo.getFreeformTasksInZOrder(DEFAULT_DISPLAY)
@@ -564,9 +580,9 @@
     @EnableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_PERSISTENCE)
     fun minimizeTask_persistenceEnabled_taskIsPersistedAsMinimized() =
         runTest(StandardTestDispatcher()) {
-            repo.addOrMoveFreeformTaskToTop(DEFAULT_DISPLAY, 5)
-            repo.addOrMoveFreeformTaskToTop(DEFAULT_DISPLAY, 6)
-            repo.addOrMoveFreeformTaskToTop(DEFAULT_DISPLAY, 7)
+            repo.addTask(DEFAULT_DISPLAY, 5, isVisible = true)
+            repo.addTask(DEFAULT_DISPLAY, 6, isVisible = true)
+            repo.addTask(DEFAULT_DISPLAY, 7, isVisible = true)
 
             repo.minimizeTask(displayId = 0, taskId = 6)
 
@@ -586,7 +602,7 @@
                     .addOrUpdateDesktop(
                         DEFAULT_USER_ID,
                         DEFAULT_DESKTOP_ID,
-                        visibleTasks = ArraySet(),
+                        visibleTasks = ArraySet(arrayOf(5)),
                         minimizedTasks = ArraySet(),
                         freeformTasksInZOrder = arrayListOf(6, 5)
                     )
@@ -594,7 +610,7 @@
                     .addOrUpdateDesktop(
                         DEFAULT_USER_ID,
                         DEFAULT_DESKTOP_ID,
-                        visibleTasks = ArraySet(),
+                        visibleTasks = ArraySet(arrayOf(5, 6)),
                         minimizedTasks = ArraySet(),
                         freeformTasksInZOrder = arrayListOf(7, 6, 5)
                     )
@@ -602,7 +618,7 @@
                     .addOrUpdateDesktop(
                         DEFAULT_USER_ID,
                         DEFAULT_DESKTOP_ID,
-                        visibleTasks = ArraySet(),
+                        visibleTasks = ArraySet(arrayOf(5, 7)),
                         minimizedTasks = ArraySet(arrayOf(6)),
                         freeformTasksInZOrder = arrayListOf(7, 6, 5)
                     )
@@ -610,10 +626,10 @@
     }
 
     @Test
-    fun addOrMoveFreeformTaskToTop_taskIsUnminimized_noop() {
-        repo.addOrMoveFreeformTaskToTop(DEFAULT_DISPLAY, 5)
-        repo.addOrMoveFreeformTaskToTop(DEFAULT_DISPLAY, 6)
-        repo.addOrMoveFreeformTaskToTop(DEFAULT_DISPLAY, 7)
+    fun addTask_taskIsUnminimized_noop() {
+        repo.addTask(DEFAULT_DISPLAY, 5, isVisible = true)
+        repo.addTask(DEFAULT_DISPLAY, 6, isVisible = true)
+        repo.addTask(DEFAULT_DISPLAY, 7, isVisible = true)
 
         val tasks = repo.getFreeformTasksInZOrder(DEFAULT_DISPLAY)
         assertThat(tasks).containsExactly(7, 6, 5).inOrder()
@@ -622,7 +638,7 @@
 
     @Test
     fun removeFreeformTask_invalidDisplay_removesTaskFromFreeformTasks() {
-        repo.addOrMoveFreeformTaskToTop(DEFAULT_DISPLAY, taskId = 1)
+        repo.addTask(DEFAULT_DISPLAY, taskId = 1, isVisible = true)
 
         repo.removeFreeformTask(INVALID_DISPLAY, taskId = 1)
 
@@ -636,7 +652,7 @@
     @EnableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_PERSISTENCE)
     fun removeFreeformTask_invalidDisplay_persistenceEnabled_removesTaskFromFreeformTasks() {
         runTest(StandardTestDispatcher()) {
-            repo.addOrMoveFreeformTaskToTop(DEFAULT_DISPLAY, taskId = 1)
+            repo.addTask(DEFAULT_DISPLAY, taskId = 1, isVisible = true)
 
             repo.removeFreeformTask(INVALID_DISPLAY, taskId = 1)
 
@@ -661,7 +677,7 @@
 
     @Test
     fun removeFreeformTask_validDisplay_removesTaskFromFreeformTasks() {
-        repo.addOrMoveFreeformTaskToTop(DEFAULT_DISPLAY, taskId = 1)
+        repo.addTask(DEFAULT_DISPLAY, taskId = 1, isVisible = true)
 
         repo.removeFreeformTask(DEFAULT_DISPLAY, taskId = 1)
 
@@ -673,7 +689,7 @@
     @EnableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_PERSISTENCE)
     fun removeFreeformTask_validDisplay_persistenceEnabled_removesTaskFromFreeformTasks() {
         runTest(StandardTestDispatcher()) {
-            repo.addOrMoveFreeformTaskToTop(DEFAULT_DISPLAY, taskId = 1)
+            repo.addTask(DEFAULT_DISPLAY, taskId = 1, isVisible = true)
 
             repo.removeFreeformTask(DEFAULT_DISPLAY, taskId = 1)
 
@@ -698,7 +714,7 @@
 
     @Test
     fun removeFreeformTask_validDisplay_differentDisplay_doesNotRemovesTask() {
-        repo.addOrMoveFreeformTaskToTop(DEFAULT_DISPLAY, taskId = 1)
+        repo.addTask(DEFAULT_DISPLAY, taskId = 1, isVisible = true)
 
         repo.removeFreeformTask(SECOND_DISPLAY, taskId = 1)
 
@@ -710,7 +726,7 @@
     @EnableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_PERSISTENCE)
     fun removeFreeformTask_validDisplayButDifferentDisplay_persistenceEnabled_doesNotRemoveTask() {
         runTest(StandardTestDispatcher()) {
-            repo.addOrMoveFreeformTaskToTop(DEFAULT_DISPLAY, taskId = 1)
+            repo.addTask(DEFAULT_DISPLAY, taskId = 1, isVisible = true)
 
             repo.removeFreeformTask(SECOND_DISPLAY, taskId = 1)
 
@@ -736,8 +752,7 @@
     @Test
     fun removeFreeformTask_removesTaskBoundsBeforeMaximize() {
         val taskId = 1
-        repo.addActiveTask(THIRD_DISPLAY, taskId)
-        repo.addOrMoveFreeformTaskToTop(THIRD_DISPLAY, taskId)
+        repo.addTask(THIRD_DISPLAY, taskId, isVisible = true)
         repo.saveBoundsBeforeMaximize(taskId, Rect(0, 0, 200, 200))
 
         repo.removeFreeformTask(THIRD_DISPLAY, taskId)
@@ -748,8 +763,7 @@
     @Test
     fun removeFreeformTask_removesTaskBoundsBeforeImmersive() {
         val taskId = 1
-        repo.addActiveTask(THIRD_DISPLAY, taskId)
-        repo.addOrMoveFreeformTaskToTop(THIRD_DISPLAY, taskId)
+        repo.addTask(THIRD_DISPLAY, taskId, isVisible = true)
         repo.saveBoundsBeforeFullImmersive(taskId, Rect(0, 0, 200, 200))
 
         repo.removeFreeformTask(THIRD_DISPLAY, taskId)
@@ -762,8 +776,7 @@
         val taskId = 1
         val listener = TestListener()
         repo.addActiveTaskListener(listener)
-        repo.addActiveTask(DEFAULT_DISPLAY, taskId)
-        repo.addOrMoveFreeformTaskToTop(DEFAULT_DISPLAY, taskId)
+        repo.addTask(DEFAULT_DISPLAY, taskId, isVisible = true)
 
         repo.removeFreeformTask(THIRD_DISPLAY, taskId)
 
@@ -774,8 +787,7 @@
     @Test
     fun removeFreeformTask_unminimizesTask() {
         val taskId = 1
-        repo.addActiveTask(DEFAULT_DISPLAY, taskId)
-        repo.addOrMoveFreeformTaskToTop(DEFAULT_DISPLAY, taskId)
+        repo.addTask(DEFAULT_DISPLAY, taskId, isVisible = true)
         repo.minimizeTask(DEFAULT_DISPLAY, taskId)
 
         repo.removeFreeformTask(DEFAULT_DISPLAY, taskId)
@@ -786,8 +798,7 @@
     @Test
     fun removeFreeformTask_updatesTaskVisibility() {
         val taskId = 1
-        repo.addActiveTask(DEFAULT_DISPLAY, taskId)
-        repo.addOrMoveFreeformTaskToTop(DEFAULT_DISPLAY, taskId)
+        repo.addTask(DEFAULT_DISPLAY, taskId, isVisible = true)
 
         repo.removeFreeformTask(THIRD_DISPLAY, taskId)
 
@@ -855,8 +866,7 @@
 
     @Test
     fun minimizeTask_withInvalidDisplay_minimizesCorrectTask() {
-        repo.addActiveTask(displayId = DEFAULT_DISPLAY, taskId = 0)
-        repo.addOrMoveFreeformTaskToTop(displayId = DEFAULT_DISPLAY, taskId = 0)
+        repo.addTask(displayId = DEFAULT_DISPLAY, taskId = 0, isVisible = true)
 
         repo.minimizeTask(displayId = INVALID_DISPLAY, taskId = 0)
 
@@ -888,9 +898,9 @@
 
 
     @Test
-    fun updateTaskVisibility_minimizedTaskBecomesVisible_unminimizesTask() {
+    fun updateTask_minimizedTaskBecomesVisible_unminimizesTask() {
         repo.minimizeTask(displayId = 10, taskId = 2)
-        repo.updateTaskVisibility(displayId = 10, taskId = 2, visible = true)
+        repo.updateTask(displayId = 10, taskId = 2, isVisible = true)
 
         val isMinimizedTask = repo.isMinimizedTask(taskId = 2)
 
@@ -899,13 +909,9 @@
 
     @Test
     fun getExpandedTasksOrdered_returnsFreeformTasksInCorrectOrder() {
-        repo.addActiveTask(displayId = DEFAULT_DISPLAY, taskId = 1)
-        repo.addActiveTask(displayId = DEFAULT_DISPLAY, taskId = 2)
-        repo.addActiveTask(displayId = DEFAULT_DISPLAY, taskId = 3)
-        // The front-most task will be the one added last through `addOrMoveFreeformTaskToTop`
-        repo.addOrMoveFreeformTaskToTop(displayId = DEFAULT_DISPLAY, taskId = 3)
-        repo.addOrMoveFreeformTaskToTop(displayId = 0, taskId = 2)
-        repo.addOrMoveFreeformTaskToTop(displayId = 0, taskId = 1)
+        repo.addTask(displayId = DEFAULT_DISPLAY, taskId = 3, isVisible = true)
+        repo.addTask(displayId = DEFAULT_DISPLAY, taskId = 2, isVisible = true)
+        repo.addTask(displayId = DEFAULT_DISPLAY, taskId = 1, isVisible = true)
 
         val tasks = repo.getExpandedTasksOrdered(displayId = 0)
 
@@ -914,13 +920,9 @@
 
     @Test
     fun getExpandedTasksOrdered_excludesMinimizedTasks() {
-        repo.addActiveTask(displayId = DEFAULT_DISPLAY, taskId = 1)
-        repo.addActiveTask(displayId = DEFAULT_DISPLAY, taskId = 2)
-        repo.addActiveTask(displayId = DEFAULT_DISPLAY, taskId = 3)
-        // The front-most task will be the one added last through `addOrMoveFreeformTaskToTop`
-        repo.addOrMoveFreeformTaskToTop(displayId = DEFAULT_DISPLAY, taskId = 3)
-        repo.addOrMoveFreeformTaskToTop(displayId = DEFAULT_DISPLAY, taskId = 2)
-        repo.addOrMoveFreeformTaskToTop(displayId = DEFAULT_DISPLAY, taskId = 1)
+        repo.addTask(displayId = DEFAULT_DISPLAY, taskId = 3, isVisible = true)
+        repo.addTask(displayId = DEFAULT_DISPLAY, taskId = 2, isVisible = true)
+        repo.addTask(displayId = DEFAULT_DISPLAY, taskId = 1, isVisible = true)
         repo.minimizeTask(displayId = DEFAULT_DISPLAY, taskId = 2)
 
         val tasks = repo.getExpandedTasksOrdered(displayId = DEFAULT_DISPLAY)
@@ -976,13 +978,10 @@
 
     @Test
     fun removeDesktop_multipleTasks_removesAll() {
-        repo.addActiveTask(displayId = DEFAULT_DISPLAY, taskId = 1)
-        repo.addActiveTask(displayId = DEFAULT_DISPLAY, taskId = 2)
-        repo.addActiveTask(displayId = DEFAULT_DISPLAY, taskId = 3)
-        // The front-most task will be the one added last through `addOrMoveFreeformTaskToTop`
-        repo.addOrMoveFreeformTaskToTop(displayId = DEFAULT_DISPLAY, taskId = 3)
-        repo.addOrMoveFreeformTaskToTop(displayId = DEFAULT_DISPLAY, taskId = 2)
-        repo.addOrMoveFreeformTaskToTop(displayId = DEFAULT_DISPLAY, taskId = 1)
+        // The front-most task will be the one added last through `addTask`.
+        repo.addTask(displayId = DEFAULT_DISPLAY, taskId = 3, isVisible = true)
+        repo.addTask(displayId = DEFAULT_DISPLAY, taskId = 2, isVisible = true)
+        repo.addTask(displayId = DEFAULT_DISPLAY, taskId = 1, isVisible = true)
         repo.minimizeTask(displayId = DEFAULT_DISPLAY, taskId = 2)
 
         val tasksBeforeRemoval = repo.removeDesktop(displayId = DEFAULT_DISPLAY)
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTaskChangeListenerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTaskChangeListenerTest.kt
new file mode 100644
index 0000000..e977966
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTaskChangeListenerTest.kt
@@ -0,0 +1,177 @@
+/*
+ * 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.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
+import android.platform.test.flag.junit.SetFlagsRule
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION
+import com.android.wm.shell.ShellTestCase
+import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createFreeformTask
+import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createFullscreenTask
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.never
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
+
+/**
+ * Tests for {@link DesktopTaskChangeListener}
+ *
+ * Build/Install/Run: atest WMShellUnitTests:DesktopTaskChangeListenerTest
+ */
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class DesktopTaskChangeListenerTest : ShellTestCase() {
+
+  @JvmField @Rule val setFlagsRule = SetFlagsRule()
+
+  private lateinit var desktopTaskChangeListener: DesktopTaskChangeListener
+
+  private val desktopRepository = mock<DesktopRepository>()
+
+  @Before
+  fun setUp() {
+    desktopTaskChangeListener = DesktopTaskChangeListener(desktopRepository)
+  }
+
+  @Test
+  fun onTaskOpening_fullscreenTask_notActiveDesktopTask_noop() {
+    val task = createFullscreenTask().apply { isVisible = true }
+    whenever(desktopRepository.isActiveTask(task.taskId)).thenReturn(false)
+
+    desktopTaskChangeListener.onTaskOpening(task)
+
+    verify(desktopRepository, never()).addTask(task.displayId, task.taskId, task.isVisible)
+    verify(desktopRepository, never()).removeFreeformTask(task.displayId, task.taskId)
+  }
+
+  @Test
+  fun onTaskOpening_freeformTask_activeDesktopTask_removesTaskFromRepo() {
+    val task = createFullscreenTask().apply { isVisible = true }
+    whenever(desktopRepository.isActiveTask(task.taskId)).thenReturn(true)
+
+    desktopTaskChangeListener.onTaskOpening(task)
+
+    verify(desktopRepository).removeFreeformTask(task.displayId, task.taskId)
+  }
+
+  @Test
+  fun onTaskOpening_freeformTask_visibleDesktopTask_addsTaskToRepository() {
+    val task = createFreeformTask().apply { isVisible = true }
+    whenever(desktopRepository.isActiveTask(task.taskId)).thenReturn(false)
+
+    desktopTaskChangeListener.onTaskOpening(task)
+
+    verify(desktopRepository).addTask(task.displayId, task.taskId, task.isVisible)
+  }
+
+  @Test
+  fun onTaskOpening_freeformTask_nonVisibleDesktopTask_addsTaskToRepository() {
+    val task = createFreeformTask().apply { isVisible = false }
+    whenever(desktopRepository.isActiveTask(task.taskId)).thenReturn(true)
+
+    desktopTaskChangeListener.onTaskOpening(task)
+
+    verify(desktopRepository).addTask(task.displayId, task.taskId, task.isVisible)
+  }
+
+  @Test
+  fun onTaskChanging_freeformTaskOutsideDesktop_removesTaskFromRepo() {
+    val task = createFullscreenTask().apply { isVisible = true }
+    whenever(desktopRepository.isActiveTask(task.taskId)).thenReturn(true)
+
+    desktopTaskChangeListener.onTaskChanging(task)
+
+    verify(desktopRepository).removeFreeformTask(task.displayId, task.taskId)
+  }
+
+  @Test
+  fun onTaskChanging_visibleTaskInDesktop_updatesTaskVisibility() {
+    val task = createFreeformTask().apply { isVisible = true }
+    whenever(desktopRepository.isActiveTask(task.taskId)).thenReturn(true)
+
+    desktopTaskChangeListener.onTaskChanging(task)
+
+    verify(desktopRepository).updateTask(task.displayId, task.taskId, task.isVisible)
+  }
+
+  @Test
+  fun onTaskChanging_nonVisibleTask_updatesTaskVisibility() {
+    val task = createFreeformTask().apply { isVisible = false }
+    whenever(desktopRepository.isActiveTask(task.taskId)).thenReturn(true)
+
+    desktopTaskChangeListener.onTaskChanging(task)
+
+    verify(desktopRepository).updateTask(task.displayId, task.taskId, task.isVisible)
+  }
+
+  @Test
+  fun onTaskMovingToFront_freeformTaskOutsideDesktop_removesTaskFromRepo() {
+    val task = createFullscreenTask().apply { isVisible = true }
+    whenever(desktopRepository.isActiveTask(task.taskId)).thenReturn(true)
+
+    desktopTaskChangeListener.onTaskMovingToFront(task)
+
+    verify(desktopRepository).removeFreeformTask(task.displayId, task.taskId)
+  }
+
+  @Test
+  @EnableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION)
+  fun onTaskClosing_backNavEnabled_nonClosingTask_minimizesTaskInRepo() {
+    val task = createFreeformTask().apply { isVisible = true }
+    whenever(desktopRepository.isActiveTask(task.taskId)).thenReturn(true)
+    whenever(desktopRepository.isClosingTask(task.taskId)).thenReturn(false)
+
+    desktopTaskChangeListener.onTaskClosing(task)
+
+    verify(desktopRepository).updateTask(task.displayId, task.taskId, isVisible = false)
+    verify(desktopRepository).minimizeTask(task.displayId, task.taskId)
+  }
+
+  @Test
+  @DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION)
+  fun onTaskClosing_backNavDisabled_closingTask_removesTaskInRepo() {
+    val task = createFreeformTask().apply { isVisible = true }
+    whenever(desktopRepository.isActiveTask(task.taskId)).thenReturn(true)
+    whenever(desktopRepository.isClosingTask(task.taskId)).thenReturn(true)
+
+    desktopTaskChangeListener.onTaskClosing(task)
+
+    verify(desktopRepository, never()).minimizeTask(task.displayId, task.taskId)
+    verify(desktopRepository).removeClosingTask(task.taskId)
+    verify(desktopRepository).removeFreeformTask(task.displayId, task.taskId)
+  }
+
+  @Test
+  @EnableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION)
+  fun onTaskClosing_backNavEnabled_closingTask_removesTaskFromRepo() {
+    val task = createFreeformTask().apply { isVisible = true }
+    whenever(desktopRepository.isActiveTask(task.taskId)).thenReturn(true)
+    whenever(desktopRepository.isClosingTask(task.taskId)).thenReturn(true)
+
+    desktopTaskChangeListener.onTaskClosing(task)
+
+    verify(desktopRepository).removeClosingTask(task.taskId)
+    verify(desktopRepository).removeFreeformTask(task.displayId, task.taskId)
+  }
+}
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 bc2b36c..018f9c2 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
@@ -110,6 +110,7 @@
 import com.android.wm.shell.desktopmode.minimize.DesktopWindowLimitRemoteHandler
 import com.android.wm.shell.desktopmode.persistence.Desktop
 import com.android.wm.shell.desktopmode.persistence.DesktopPersistentRepository
+import com.android.wm.shell.desktopmode.persistence.DesktopRepositoryInitializer
 import com.android.wm.shell.draganddrop.DragAndDropController
 import com.android.wm.shell.freeform.FreeformTaskTransitionStarter
 import com.android.wm.shell.recents.RecentTasksController
@@ -128,6 +129,8 @@
 import com.android.wm.shell.transition.Transitions
 import com.android.wm.shell.transition.Transitions.ENABLE_SHELL_TRANSITIONS
 import com.android.wm.shell.transition.Transitions.TransitionHandler
+import com.android.wm.shell.windowdecor.DesktopModeWindowDecoration
+import com.android.wm.shell.windowdecor.tiling.DesktopTilingDecorViewModel
 import com.google.common.truth.Truth.assertThat
 import com.google.common.truth.Truth.assertWithMessage
 import java.util.function.Consumer
@@ -194,6 +197,7 @@
   @Mock lateinit var transitions: Transitions
   @Mock lateinit var keyguardManager: KeyguardManager
   @Mock lateinit var mReturnToDragStartAnimator: ReturnToDragStartAnimator
+  @Mock lateinit var desktopMixedTransitionHandler: DesktopMixedTransitionHandler
   @Mock lateinit var exitDesktopTransitionHandler: ExitDesktopTaskTransitionHandler
   @Mock lateinit var enterDesktopTransitionHandler: EnterDesktopTaskTransitionHandler
   @Mock lateinit var dragAndDropTransitionHandler: DesktopModeDragAndDropTransitionHandler
@@ -221,8 +225,13 @@
   @Mock private lateinit var mockInputManager: InputManager
   @Mock private lateinit var mockFocusTransitionObserver: FocusTransitionObserver
   @Mock lateinit var motionEvent: MotionEvent
+  @Mock lateinit var repositoryInitializer: DesktopRepositoryInitializer
 
   private lateinit var mockitoSession: StaticMockitoSession
+  @Mock
+  private lateinit var desktopTilingDecorViewModel: DesktopTilingDecorViewModel
+  @Mock
+  private lateinit var desktopWindowDecoration: DesktopModeWindowDecoration
   private lateinit var controller: DesktopTasksController
   private lateinit var shellInit: ShellInit
   private lateinit var taskRepository: DesktopRepository
@@ -257,7 +266,8 @@
 
     testScope = CoroutineScope(Dispatchers.Unconfined + SupervisorJob())
     shellInit = spy(ShellInit(testExecutor))
-    taskRepository = DesktopRepository(context, shellInit, persistentRepository, testScope)
+    taskRepository =
+      DesktopRepository(context, shellInit, persistentRepository, repositoryInitializer, testScope)
     desktopTasksLimiter =
         DesktopTasksLimiter(
             transitions,
@@ -282,6 +292,12 @@
     val tda = DisplayAreaInfo(MockToken().token(), DEFAULT_DISPLAY, 0)
     tda.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FULLSCREEN
     whenever(rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)).thenReturn(tda)
+    whenever(mMockDesktopImmersiveController
+      .exitImmersiveIfApplicable(any(), any<RunningTaskInfo>()))
+      .thenReturn(DesktopImmersiveController.ExitResult.NoExit)
+    whenever(mMockDesktopImmersiveController
+      .exitImmersiveIfApplicable(any(), anyInt(), anyOrNull()))
+      .thenReturn(DesktopImmersiveController.ExitResult.NoExit)
 
     controller = createController()
     controller.setSplitScreenController(splitScreenController)
@@ -317,6 +333,7 @@
         transitions,
         keyguardManager,
         mReturnToDragStartAnimator,
+        desktopMixedTransitionHandler,
         enterDesktopTransitionHandler,
         exitDesktopTransitionHandler,
         dragAndDropTransitionHandler,
@@ -336,6 +353,7 @@
         mockInputManager,
         mockFocusTransitionObserver,
         desktopModeEventLogger,
+        desktopTilingDecorViewModel,
       )
   }
 
@@ -1382,7 +1400,7 @@
 
     controller.moveTaskToFront(task1, remoteTransition = null)
 
-    val wct = getLatestWct(type = TRANSIT_TO_FRONT)
+    val wct = getLatestDesktopMixedTaskWct(type = TRANSIT_TO_FRONT)
     assertThat(wct.hierarchyOps).hasSize(1)
     wct.assertReorderAt(index = 0, task1)
   }
@@ -1391,10 +1409,16 @@
   fun moveTaskToFront_bringsTasksOverLimit_minimizesBackTask() {
     setUpHomeTask()
     val freeformTasks = (1..MAX_TASK_LIMIT + 1).map { _ -> setUpFreeformTask() }
+    whenever(desktopMixedTransitionHandler.startLaunchTransition(
+      eq(TRANSIT_TO_FRONT),
+      any(),
+      eq(freeformTasks[0].taskId),
+      anyOrNull()
+    )).thenReturn(Binder())
 
     controller.moveTaskToFront(freeformTasks[0], remoteTransition = null)
 
-    val wct = getLatestWct(type = TRANSIT_TO_FRONT)
+    val wct = getLatestDesktopMixedTaskWct(type = TRANSIT_TO_FRONT)
     assertThat(wct.hierarchyOps.size).isEqualTo(2) // move-to-front + minimize
     wct.assertReorderAt(0, freeformTasks[0], toTop = true)
     wct.assertReorderAt(1, freeformTasks[1], toTop = false)
@@ -1436,7 +1460,7 @@
 
     controller.moveTaskToFront(task.taskId, remoteTransition = null)
 
-    val wct = getLatestWct(type = TRANSIT_OPEN)
+    val wct = getLatestDesktopMixedTaskWct(type = TRANSIT_OPEN)
     assertThat(wct.hierarchyOps).hasSize(1)
     wct.assertLaunchTaskAt(0, task.taskId, WINDOWING_MODE_FREEFORM)
   }
@@ -1446,10 +1470,13 @@
     val freeformTasks = (1..MAX_TASK_LIMIT).map { _ -> setUpFreeformTask() }
     val task = createTaskInfo(1001)
     whenever(shellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(null)
+    whenever(desktopMixedTransitionHandler
+      .startLaunchTransition(eq(TRANSIT_OPEN), any(), eq(task.taskId), anyOrNull()))
+      .thenReturn(Binder())
 
     controller.moveTaskToFront(task.taskId, remoteTransition = null)
 
-    val wct = getLatestWct(type = TRANSIT_OPEN)
+    val wct = getLatestDesktopMixedTaskWct(type = TRANSIT_OPEN)
     assertThat(wct.hierarchyOps.size).isEqualTo(2) // launch + minimize
     wct.assertLaunchTaskAt(0, task.taskId, WINDOWING_MODE_FREEFORM)
     wct.assertReorderAt(1, freeformTasks[0], toTop = false)
@@ -1688,7 +1715,7 @@
   }
 
   @Test
-  fun onDesktopWindowMinimize_singleActiveTask_hasWallpaperActivityToken_removesWallpaper() {
+  fun onTaskMinimize_singleActiveTask_hasWallpaperActivityToken_removesWallpaper() {
     val task = setUpFreeformTask()
     val transition = Binder()
     whenever(freeformTaskTransitionStarter.startMinimizedModeTransition(any()))
@@ -1784,7 +1811,10 @@
     whenever(freeformTaskTransitionStarter.startMinimizedModeTransition(any()))
       .thenReturn(transition)
     whenever(mMockDesktopImmersiveController.exitImmersiveIfApplicable(any(), eq(task)))
-      .thenReturn(runOnTransit)
+      .thenReturn(DesktopImmersiveController.ExitResult.Exit(
+        exitingTask = task.taskId,
+        runOnTransitionStart = runOnTransit,
+      ))
 
     controller.minimizeTask(task)
 
@@ -2375,8 +2405,7 @@
     taskRepository.wallpaperActivityToken = wallpaperToken
     taskRepository.minimizeTask(displayId = DEFAULT_DISPLAY, taskId = task2.taskId)
     // Task is being minimized so mark it as not visible.
-    taskRepository
-      .updateTaskVisibility(displayId = DEFAULT_DISPLAY, task2.taskId, false)
+    taskRepository.updateTask(displayId = DEFAULT_DISPLAY, task2.taskId, isVisible = false)
     val result = controller.handleRequest(Binder(), createTransition(task2, type = TRANSIT_TO_BACK))
 
     assertNull(result, "Should not handle request")
@@ -2490,8 +2519,7 @@
     taskRepository.wallpaperActivityToken = wallpaperToken
     taskRepository.minimizeTask(displayId = DEFAULT_DISPLAY, taskId = task2.taskId)
     // Task is being minimized so mark it as not visible.
-    taskRepository
-      .updateTaskVisibility(displayId = DEFAULT_DISPLAY, task2.taskId, false)
+    taskRepository.updateTask(displayId = DEFAULT_DISPLAY, task2.taskId, isVisible = false)
     val result = controller.handleRequest(Binder(), createTransition(task2, type = TRANSIT_TO_BACK))
 
     assertNull(result, "Should not handle request")
@@ -2566,8 +2594,7 @@
     task3.isFocused = false
     taskRepository.wallpaperActivityToken = wallpaperToken
     taskRepository.minimizeTask(DEFAULT_DISPLAY, task1.taskId)
-    taskRepository.updateTaskVisibility(DEFAULT_DISPLAY, task3.taskId,
-      visible = false)
+    taskRepository.updateTask(DEFAULT_DISPLAY, task3.taskId, isVisible = false)
 
     controller.enterFullscreen(DEFAULT_DISPLAY, transitionSource = UNKNOWN)
 
@@ -2835,7 +2862,9 @@
         Rect(100, -100, 500, 1000), /* currentDragBounds */
         Rect(0, 50, 2000, 2000), /* validDragArea */
         Rect() /* dragStartBounds */,
-        motionEvent)
+        motionEvent,
+        desktopWindowDecoration,
+        )
     val rectAfterEnd = Rect(100, 50, 500, 1150)
     verify(transitions)
         .startTransition(
@@ -2871,7 +2900,9 @@
       currentDragBounds, /* currentDragBounds */
       Rect(0, 50, 2000, 2000) /* validDragArea */,
       Rect() /* dragStartBounds */,
-      motionEvent)
+      motionEvent,
+      desktopWindowDecoration,
+      )
 
 
     verify(transitions)
@@ -2886,6 +2917,123 @@
   }
 
   @Test
+  fun onDesktopDragEnd_fullscreenIndicator_dragToExitDesktop() {
+    val task = setUpFreeformTask(bounds = Rect(0, 0, 100, 100))
+    val spyController = spy(controller)
+    val mockSurface = mock(SurfaceControl::class.java)
+    val mockDisplayLayout = mock(DisplayLayout::class.java)
+    val tda = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)!!
+    tda.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FULLSCREEN
+    whenever(displayController.getDisplayLayout(task.displayId)).thenReturn(mockDisplayLayout)
+    whenever(mockDisplayLayout.stableInsets()).thenReturn(Rect(0, 100, 2000, 2000))
+    whenever(mockDisplayLayout.getStableBounds(any())).thenAnswer { i ->
+      (i.arguments.first() as Rect).set(STABLE_BOUNDS)
+    }
+    whenever(DesktopModeStatus.shouldMaximizeWhenDragToTopEdge(context)).thenReturn(false)
+    whenever(spyController.getVisualIndicator()).thenReturn(desktopModeVisualIndicator)
+    whenever(desktopModeVisualIndicator.updateIndicatorType(anyOrNull()))
+      .thenReturn(DesktopModeVisualIndicator.IndicatorType.TO_FULLSCREEN_INDICATOR)
+
+    // Drag move the task to the top edge
+    spyController.onDragPositioningMove(task, mockSurface, 200f, Rect(100, 200, 500, 1000))
+    spyController.onDragPositioningEnd(
+      task,
+      mockSurface,
+      Point(100, 50), /* position */
+      PointF(200f, 300f), /* inputCoordinate */
+      Rect(100, 50, 500, 1000), /* currentDragBounds */
+      Rect(0, 50, 2000, 2000) /* validDragArea */,
+      Rect() /* dragStartBounds */,
+      motionEvent,
+      desktopWindowDecoration)
+
+    // Assert the task exits desktop mode
+    val wct = getLatestExitDesktopWct()
+    assertThat(wct.changes[task.token.asBinder()]?.windowingMode)
+        .isEqualTo(WINDOWING_MODE_UNDEFINED)
+  }
+
+  @Test
+  fun onDesktopDragEnd_fullscreenIndicator_dragToMaximize() {
+    val task = setUpFreeformTask(bounds = Rect(0, 0, 100, 100))
+    val spyController = spy(controller)
+    val mockSurface = mock(SurfaceControl::class.java)
+    val mockDisplayLayout = mock(DisplayLayout::class.java)
+    val tda = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)!!
+    tda.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FULLSCREEN
+    whenever(displayController.getDisplayLayout(task.displayId)).thenReturn(mockDisplayLayout)
+    whenever(mockDisplayLayout.stableInsets()).thenReturn(Rect(0, 100, 2000, 2000))
+    whenever(mockDisplayLayout.getStableBounds(any())).thenAnswer { i ->
+      (i.arguments.first() as Rect).set(STABLE_BOUNDS)
+    }
+    whenever(DesktopModeStatus.shouldMaximizeWhenDragToTopEdge(context)).thenReturn(true)
+    whenever(spyController.getVisualIndicator()).thenReturn(desktopModeVisualIndicator)
+    whenever(desktopModeVisualIndicator.updateIndicatorType(anyOrNull()))
+      .thenReturn(DesktopModeVisualIndicator.IndicatorType.TO_FULLSCREEN_INDICATOR)
+
+    // Drag move the task to the top edge
+    spyController.onDragPositioningMove(task, mockSurface, 200f, Rect(100, 200, 500, 1000))
+    spyController.onDragPositioningEnd(
+      task,
+      mockSurface,
+      Point(100, 50), /* position */
+      PointF(200f, 300f), /* inputCoordinate */
+      Rect(100, 50, 500, 1000), /* currentDragBounds */
+      Rect(0, 50, 2000, 2000) /* validDragArea */,
+      Rect() /* dragStartBounds */,
+      motionEvent,
+      desktopWindowDecoration)
+
+    // Assert bounds set to stable bounds
+    val wct = getLatestToggleResizeDesktopTaskWct()
+    assertThat(findBoundsChange(wct, task)).isEqualTo(STABLE_BOUNDS)
+  }
+
+  @Test
+  fun onDesktopDragEnd_fullscreenIndicator_dragToMaximize_noBoundsChange() {
+    val task = setUpFreeformTask(bounds = STABLE_BOUNDS)
+    val spyController = spy(controller)
+    val mockSurface = mock(SurfaceControl::class.java)
+    val mockDisplayLayout = mock(DisplayLayout::class.java)
+    val tda = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)!!
+    tda.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FULLSCREEN
+    whenever(displayController.getDisplayLayout(task.displayId)).thenReturn(mockDisplayLayout)
+    whenever(mockDisplayLayout.stableInsets()).thenReturn(Rect(0, 100, 2000, 2000))
+    whenever(mockDisplayLayout.getStableBounds(any())).thenAnswer { i ->
+      (i.arguments.first() as Rect).set(STABLE_BOUNDS)
+    }
+    whenever(DesktopModeStatus.shouldMaximizeWhenDragToTopEdge(context)).thenReturn(true)
+    whenever(spyController.getVisualIndicator()).thenReturn(desktopModeVisualIndicator)
+    whenever(desktopModeVisualIndicator.updateIndicatorType(anyOrNull()))
+      .thenReturn(DesktopModeVisualIndicator.IndicatorType.TO_FULLSCREEN_INDICATOR)
+
+    // Drag move the task to the top edge
+    val currentDragBounds = Rect(100, 50, 500, 1000)
+    spyController.onDragPositioningMove(task, mockSurface, 200f, Rect(100, 200, 500, 1000))
+    spyController.onDragPositioningEnd(
+      task,
+      mockSurface,
+      Point(100, 50), /* position */
+      PointF(200f, 300f), /* inputCoordinate */
+      currentDragBounds, /* currentDragBounds */
+      Rect(0, 50, 2000, 2000) /* validDragArea */,
+      Rect() /* dragStartBounds */,
+      motionEvent,
+      desktopWindowDecoration)
+
+    // Assert that task is NOT updated via WCT
+    verify(toggleResizeDesktopTaskTransitionHandler, never()).startTransition(any(), any())
+    // Assert that task leash is updated via Surface Animations
+    verify(mReturnToDragStartAnimator).start(
+      eq(task.taskId),
+      eq(mockSurface),
+      eq(currentDragBounds),
+      eq(STABLE_BOUNDS),
+      eq(true)
+    )
+  }
+
+  @Test
   fun enterSplit_freeformTaskIsMovedToSplit() {
     val task1 = setUpFreeformTask()
     val task2 = setUpFreeformTask()
@@ -2917,8 +3065,7 @@
     task3.isFocused = false
     taskRepository.wallpaperActivityToken = wallpaperToken
     taskRepository.minimizeTask(DEFAULT_DISPLAY, task1.taskId)
-    taskRepository.updateTaskVisibility(DEFAULT_DISPLAY, task3.taskId,
-      visible = false)
+    taskRepository.updateTask(DEFAULT_DISPLAY, task3.taskId, isVisible = false)
 
     controller.enterSplit(DEFAULT_DISPLAY, leftOrTop = false)
 
@@ -3094,7 +3241,10 @@
       .thenReturn(transition)
     whenever(mMockDesktopImmersiveController
       .exitImmersiveIfApplicable(any(), eq(immersiveTask.displayId), eq(freeformTask.taskId)))
-      .thenReturn(runOnStartTransit)
+      .thenReturn(DesktopImmersiveController.ExitResult.Exit(
+        exitingTask = immersiveTask.taskId,
+        runOnTransitionStart = runOnStartTransit,
+      ))
 
     runOpenInstance(immersiveTask, freeformTask.taskId)
 
@@ -3135,6 +3285,7 @@
   }
 
   @Test
+  @DisableFlags(Flags.FLAG_ENABLE_TILE_RESIZING)
   fun snapToHalfScreen_getSnapBounds_calculatesBoundsForResizable() {
     val bounds = Rect(100, 100, 300, 300)
     val task = setUpFreeformTask(DEFAULT_DISPLAY, bounds).apply {
@@ -3150,7 +3301,8 @@
       STABLE_BOUNDS.left, STABLE_BOUNDS.top, STABLE_BOUNDS.right / 2, STABLE_BOUNDS.bottom
     )
 
-    controller.snapToHalfScreen(task, mockSurface, currentDragBounds, SnapPosition.LEFT, ResizeTrigger.SNAP_LEFT_MENU, motionEvent)
+    controller.snapToHalfScreen(task, mockSurface, currentDragBounds, SnapPosition.LEFT,
+      ResizeTrigger.SNAP_LEFT_MENU, motionEvent, desktopWindowDecoration)
     // Assert bounds set to stable bounds
     val wct = getLatestToggleResizeDesktopTaskWct(currentDragBounds)
     assertThat(findBoundsChange(wct, task)).isEqualTo(expectedBounds)
@@ -3165,6 +3317,7 @@
   }
 
   @Test
+  @DisableFlags(Flags.FLAG_ENABLE_TILE_RESIZING)
   fun snapToHalfScreen_snapBoundsWhenAlreadySnapped_animatesSurfaceWithoutWCT() {
     // Set up task to already be in snapped-left bounds
     val bounds = Rect(
@@ -3180,8 +3333,8 @@
 
     // Attempt to snap left again
     val currentDragBounds = Rect(bounds).apply { offset(-100, 0) }
-    controller.snapToHalfScreen(task, mockSurface, currentDragBounds, SnapPosition.LEFT, ResizeTrigger.SNAP_LEFT_MENU, motionEvent)
-
+    controller.snapToHalfScreen(task, mockSurface, currentDragBounds, SnapPosition.LEFT,
+      ResizeTrigger.SNAP_LEFT_MENU, motionEvent, desktopWindowDecoration)
     // Assert that task is NOT updated via WCT
     verify(toggleResizeDesktopTaskTransitionHandler, never()).startTransition(any(), any())
 
@@ -3204,7 +3357,7 @@
   }
 
   @Test
-  @DisableFlags(Flags.FLAG_DISABLE_NON_RESIZABLE_APP_SNAP_RESIZING)
+  @DisableFlags(Flags.FLAG_DISABLE_NON_RESIZABLE_APP_SNAP_RESIZING, Flags.FLAG_ENABLE_TILE_RESIZING)
   fun handleSnapResizingTask_nonResizable_snapsToHalfScreen() {
     val task = setUpFreeformTask(DEFAULT_DISPLAY, Rect(0, 0, 200, 100)).apply {
       isResizeable = false
@@ -3215,7 +3368,9 @@
       Rect(STABLE_BOUNDS.left, STABLE_BOUNDS.top, STABLE_BOUNDS.right / 2, STABLE_BOUNDS.bottom)
 
     controller.handleSnapResizingTask(
-      task, SnapPosition.LEFT, mockSurface, currentDragBounds, preDragBounds, motionEvent
+
+      task, SnapPosition.LEFT, mockSurface, currentDragBounds, preDragBounds, motionEvent,
+      desktopWindowDecoration
     )
     val wct = getLatestToggleResizeDesktopTaskWct(currentDragBounds)
     assertThat(findBoundsChange(wct, task)).isEqualTo(
@@ -3239,7 +3394,8 @@
     val currentDragBounds = Rect(0, 100, 300, 500)
 
     controller.handleSnapResizingTask(
-      task, SnapPosition.LEFT, mockSurface, currentDragBounds, preDragBounds, motionEvent)
+      task, SnapPosition.LEFT, mockSurface, currentDragBounds, preDragBounds, motionEvent,
+      desktopWindowDecoration)
     verify(mReturnToDragStartAnimator).start(
       eq(task.taskId),
       eq(mockSurface),
@@ -3405,7 +3561,6 @@
     )
   }
 
-
   @Test
   fun onUnhandledDrag_newFreeformIntent() {
     testOnUnhandledDrag(DesktopModeVisualIndicator.IndicatorType.TO_DESKTOP_INDICATOR,
@@ -3490,7 +3645,11 @@
     val runOnStartTransit = RunOnStartTransitionCallback()
     val transition = Binder()
     whenever(mMockDesktopImmersiveController
-      .exitImmersiveIfApplicable(wct, task.displayId, task.taskId)).thenReturn(runOnStartTransit)
+      .exitImmersiveIfApplicable(wct, task.displayId, task.taskId))
+      .thenReturn(DesktopImmersiveController.ExitResult.Exit(
+        exitingTask = 5,
+        runOnTransitionStart = runOnStartTransit,
+      ))
     whenever(enterDesktopTransitionHandler.moveToDesktop(wct, UNKNOWN)).thenReturn(transition)
 
     controller.moveTaskToDesktop(taskId = task.taskId, wct = wct, transitionSource = UNKNOWN)
@@ -3507,7 +3666,11 @@
     val runOnStartTransit = RunOnStartTransitionCallback()
     val transition = Binder()
     whenever(mMockDesktopImmersiveController
-      .exitImmersiveIfApplicable(wct, task.displayId, task.taskId)).thenReturn(runOnStartTransit)
+      .exitImmersiveIfApplicable(wct, task.displayId, task.taskId))
+      .thenReturn(DesktopImmersiveController.ExitResult.Exit(
+        exitingTask = 5,
+        runOnTransitionStart = runOnStartTransit,
+      ))
     whenever(enterDesktopTransitionHandler.moveToDesktop(wct, UNKNOWN)).thenReturn(transition)
 
     controller.moveTaskToDesktop(taskId = task.taskId, wct = wct, transitionSource = UNKNOWN)
@@ -3524,8 +3687,12 @@
     val transition = Binder()
     whenever(mMockDesktopImmersiveController
       .exitImmersiveIfApplicable(any(), eq(task.displayId), eq(task.taskId)))
-      .thenReturn(runOnStartTransit)
-    whenever(transitions.startTransition(any(), any(), anyOrNull())).thenReturn(transition)
+      .thenReturn(DesktopImmersiveController.ExitResult.Exit(
+        exitingTask = 5,
+        runOnTransitionStart = runOnStartTransit,
+      ))
+    whenever(desktopMixedTransitionHandler
+      .startLaunchTransition(any(), any(), anyInt(), anyOrNull())).thenReturn(transition)
 
     controller.moveTaskToFront(task.taskId, remoteTransition = null)
 
@@ -3541,8 +3708,13 @@
     val transition = Binder()
     whenever(mMockDesktopImmersiveController
       .exitImmersiveIfApplicable(any(), eq(task.displayId), eq(task.taskId)))
-      .thenReturn(runOnStartTransit)
-    whenever(transitions.startTransition(any(), any(), anyOrNull())).thenReturn(transition)
+      .thenReturn(DesktopImmersiveController.ExitResult.Exit(
+        exitingTask = 5,
+        runOnTransitionStart = runOnStartTransit,
+      ))
+    whenever(desktopMixedTransitionHandler
+      .startLaunchTransition(any(), any(), eq(task.taskId), anyOrNull()))
+      .thenReturn(transition)
 
     controller.moveTaskToFront(task.taskId, remoteTransition = null)
 
@@ -3791,11 +3963,7 @@
     } else {
       whenever(shellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(task)
     }
-    if (active) {
-      taskRepository.addActiveTask(displayId, task.taskId)
-      taskRepository.updateTaskVisibility(displayId, task.taskId, visible = true)
-    }
-    taskRepository.addOrMoveFreeformTaskToTop(displayId, task.taskId)
+    taskRepository.addTask(displayId, task.taskId, isVisible = active)
     if (!background) {
       runningTasks.add(task)
     }
@@ -3898,13 +4066,11 @@
   }
 
   private fun markTaskVisible(task: RunningTaskInfo) {
-    taskRepository.updateTaskVisibility(
-        task.displayId, task.taskId, visible = true)
+    taskRepository.updateTask(task.displayId, task.taskId, isVisible = true)
   }
 
   private fun markTaskHidden(task: RunningTaskInfo) {
-    taskRepository.updateTaskVisibility(
-        task.displayId, task.taskId, visible = false)
+    taskRepository.updateTask(task.displayId, task.taskId, isVisible = false)
   }
 
   private fun getLatestWct(
@@ -3912,7 +4078,6 @@
       handlerClass: Class<out TransitionHandler>? = null
   ): WindowContainerTransaction {
     val arg = ArgumentCaptor.forClass(WindowContainerTransaction::class.java)
-
     if (handlerClass == null) {
       verify(transitions).startTransition(eq(type), arg.capture(), isNull())
     } else {
@@ -3931,6 +4096,16 @@
     return arg.value
   }
 
+  private fun getLatestDesktopMixedTaskWct(
+    @WindowManager.TransitionType type: Int = TRANSIT_OPEN,
+  ): WindowContainerTransaction {
+    val arg: ArgumentCaptor<WindowContainerTransaction> =
+      ArgumentCaptor.forClass(WindowContainerTransaction::class.java)
+    verify(desktopMixedTransitionHandler)
+      .startLaunchTransition(eq(type), capture(arg), anyInt(), anyOrNull())
+    return arg.value
+  }
+
   private fun getLatestEnterDesktopWct(): WindowContainerTransaction {
     val arg = ArgumentCaptor.forClass(WindowContainerTransaction::class.java)
     verify(enterDesktopTransitionHandler).moveToDesktop(arg.capture(), any())
@@ -4029,7 +4204,6 @@
 }
 
 private fun WindowContainerTransaction.hasRemoveAt(index: Int, token: WindowContainerToken) {
-
   assertIndexInBounds(index)
   val op = hierarchyOps[index]
   assertThat(op.type).isEqualTo(HIERARCHY_OP_TYPE_REMOVE_TASK)
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 fa878d0..01b69ae 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
@@ -41,6 +41,7 @@
 import com.android.wm.shell.common.ShellExecutor
 import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createFreeformTask
 import com.android.wm.shell.desktopmode.persistence.DesktopPersistentRepository
+import com.android.wm.shell.desktopmode.persistence.DesktopRepositoryInitializer
 import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
 import com.android.wm.shell.sysui.ShellInit
 import com.android.wm.shell.transition.TransitionInfoBuilder
@@ -89,6 +90,7 @@
     @Mock lateinit var handler: Handler
     @Mock lateinit var testExecutor: ShellExecutor
     @Mock lateinit var persistentRepository: DesktopPersistentRepository
+    @Mock lateinit var repositoryInitializer: DesktopRepositoryInitializer
 
     private lateinit var mockitoSession: StaticMockitoSession
     private lateinit var desktopTasksLimiter: DesktopTasksLimiter
@@ -106,7 +108,13 @@
         testScope = CoroutineScope(Dispatchers.Unconfined + SupervisorJob())
 
         desktopTaskRepo =
-            DesktopRepository(context, shellInit, persistentRepository, testScope)
+            DesktopRepository(
+                context,
+                shellInit,
+                persistentRepository,
+                repositoryInitializer,
+                testScope
+            )
         desktopTasksLimiter =
             DesktopTasksLimiter(transitions, desktopTaskRepo, shellTaskOrganizer, MAX_TASK_LIMIT,
                 interactionJankMonitor, mContext, handler)
@@ -248,8 +256,8 @@
     @Test
     @DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION)
     fun removeLeftoverMinimizedTasks_activeNonMinimizedTasksStillAround_doesNothing() {
-        desktopTaskRepo.addActiveTask(displayId = DEFAULT_DISPLAY, taskId = 1)
-        desktopTaskRepo.addActiveTask(displayId = DEFAULT_DISPLAY, taskId = 2)
+        desktopTaskRepo.addTask(displayId = DEFAULT_DISPLAY, taskId = 1, isVisible = true)
+        desktopTaskRepo.addTask(displayId = DEFAULT_DISPLAY, taskId = 2, isVisible = true)
         desktopTaskRepo.minimizeTask(displayId = DEFAULT_DISPLAY, taskId = 2)
 
         val wct = WindowContainerTransaction()
@@ -329,7 +337,7 @@
                         wct = wct,
                         newFrontTaskId = setUpFreeformTask().taskId)
 
-        assertThat(minimizedTaskId).isEqualTo(tasks.first())
+        assertThat(minimizedTaskId).isEqualTo(tasks.first().taskId)
         assertThat(wct.hierarchyOps.size).isEqualTo(1)
         assertThat(wct.hierarchyOps[0].type).isEqualTo(HIERARCHY_OP_TYPE_REORDER)
         assertThat(wct.hierarchyOps[0].toTop).isFalse() // Reorder to bottom
@@ -355,7 +363,7 @@
     fun getTaskToMinimize_tasksWithinLimit_returnsNull() {
         val tasks = (1..MAX_TASK_LIMIT).map { setUpFreeformTask() }
 
-        val minimizedTask = desktopTasksLimiter.getTaskToMinimize(
+        val minimizedTask = desktopTasksLimiter.getTaskIdToMinimize(
                 visibleOrderedTasks = tasks.map { it.taskId })
 
         assertThat(minimizedTask).isNull()
@@ -365,11 +373,11 @@
     fun getTaskToMinimize_tasksAboveLimit_returnsBackTask() {
         val tasks = (1..MAX_TASK_LIMIT + 1).map { setUpFreeformTask() }
 
-        val minimizedTask = desktopTasksLimiter.getTaskToMinimize(
+        val minimizedTask = desktopTasksLimiter.getTaskIdToMinimize(
                 visibleOrderedTasks = tasks.map { it.taskId })
 
         // first == front, last == back
-        assertThat(minimizedTask).isEqualTo(tasks.last())
+        assertThat(minimizedTask).isEqualTo(tasks.last().taskId)
     }
 
     @Test
@@ -379,23 +387,23 @@
                 interactionJankMonitor, mContext, handler)
         val tasks = (1..MAX_TASK_LIMIT2 + 1).map { setUpFreeformTask() }
 
-        val minimizedTask = desktopTasksLimiter.getTaskToMinimize(
+        val minimizedTask = desktopTasksLimiter.getTaskIdToMinimize(
             visibleOrderedTasks = tasks.map { it.taskId })
 
         // first == front, last == back
-        assertThat(minimizedTask).isEqualTo(tasks.last())
+        assertThat(minimizedTask).isEqualTo(tasks.last().taskId)
     }
 
     @Test
     fun getTaskToMinimize_withNewTask_tasksAboveLimit_returnsBackTask() {
         val tasks = (1..MAX_TASK_LIMIT).map { setUpFreeformTask() }
 
-        val minimizedTask = desktopTasksLimiter.getTaskToMinimize(
+        val minimizedTask = desktopTasksLimiter.getTaskIdToMinimize(
                 visibleOrderedTasks = tasks.map { it.taskId },
                 newTaskIdInFront = setUpFreeformTask().taskId)
 
         // first == front, last == back
-        assertThat(minimizedTask).isEqualTo(tasks.last())
+        assertThat(minimizedTask).isEqualTo(tasks.last().taskId)
     }
 
     @Test
@@ -487,29 +495,26 @@
         verify(interactionJankMonitor).end(eq(CUJ_DESKTOP_MODE_MINIMIZE_WINDOW))
     }
 
-    private fun setUpFreeformTask(
-            displayId: Int = DEFAULT_DISPLAY,
-    ): RunningTaskInfo {
+    private fun setUpFreeformTask(displayId: Int = DEFAULT_DISPLAY): RunningTaskInfo {
         val task = createFreeformTask(displayId)
         `when`(shellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(task)
-        desktopTaskRepo.addActiveTask(displayId, task.taskId)
-        desktopTaskRepo.addOrMoveFreeformTaskToTop(displayId, task.taskId)
+        desktopTaskRepo.addTask(displayId, task.taskId, task.isVisible)
         return task
     }
 
     private fun markTaskVisible(task: RunningTaskInfo) {
-        desktopTaskRepo.updateTaskVisibility(
+        desktopTaskRepo.updateTask(
                 task.displayId,
                 task.taskId,
-                visible = true
+                isVisible = true
         )
     }
 
     private fun markTaskHidden(task: RunningTaskInfo) {
-        desktopTaskRepo.updateTaskVisibility(
+        desktopTaskRepo.updateTask(
                 task.displayId,
                 task.taskId,
-                visible = false
+                isVisible = false
         )
     }
 
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/persistence/DesktopRepositoryInitializerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/persistence/DesktopRepositoryInitializerTest.kt
new file mode 100644
index 0000000..9753429
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/persistence/DesktopRepositoryInitializerTest.kt
@@ -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.wm.shell.desktopmode.persistence
+
+import android.platform.test.annotations.EnableFlags
+import android.testing.AndroidTestingRunner
+import android.view.Display.DEFAULT_DISPLAY
+import androidx.test.filters.SmallTest
+import com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_WINDOWING_PERSISTENCE
+import com.android.wm.shell.ShellTestCase
+import com.android.wm.shell.common.ShellExecutor
+import com.android.wm.shell.desktopmode.DesktopRepository
+import com.android.wm.shell.sysui.ShellInit
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.SupervisorJob
+import kotlinx.coroutines.cancel
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.runTest
+import kotlinx.coroutines.test.setMain
+import org.junit.After
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito.inOrder
+import org.mockito.Mockito.spy
+import org.mockito.kotlin.any
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@ExperimentalCoroutinesApi
+class DesktopRepositoryInitializerTest : ShellTestCase() {
+
+    private lateinit var repositoryInitializer: DesktopRepositoryInitializer
+    private lateinit var shellInit: ShellInit
+    private lateinit var datastoreScope: CoroutineScope
+
+    private lateinit var desktopRepository: DesktopRepository
+    private val persistentRepository = mock<DesktopPersistentRepository>()
+    private val testExecutor = mock<ShellExecutor>()
+
+    @Before
+    fun setUp() {
+        Dispatchers.setMain(StandardTestDispatcher())
+        shellInit = spy(ShellInit(testExecutor))
+        datastoreScope = CoroutineScope(Dispatchers.Unconfined + SupervisorJob())
+        repositoryInitializer =
+            DesktopRepositoryInitializerImpl(context, persistentRepository, datastoreScope)
+        desktopRepository =
+            DesktopRepository(
+                context, shellInit, persistentRepository, repositoryInitializer, datastoreScope)
+    }
+
+    @Test
+    @EnableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_PERSISTENCE)
+    fun initWithPersistence_multipleTasks_addedCorrectly() =
+        runTest(StandardTestDispatcher()) {
+            val freeformTasksInZOrder = listOf(1, 2, 3)
+            whenever(persistentRepository.readDesktop(any(), any()))
+                .thenReturn(
+                    Desktop.newBuilder()
+                        .setDesktopId(1)
+                        .addAllZOrderedTasks(freeformTasksInZOrder)
+                        .putTasksByTaskId(
+                            1,
+                            DesktopTask.newBuilder()
+                                .setTaskId(1)
+                                .setDesktopTaskState(DesktopTaskState.VISIBLE)
+                                .build())
+                        .putTasksByTaskId(
+                            2,
+                            DesktopTask.newBuilder()
+                                .setTaskId(2)
+                                .setDesktopTaskState(DesktopTaskState.VISIBLE)
+                                .build())
+                        .putTasksByTaskId(
+                            3,
+                            DesktopTask.newBuilder()
+                                .setTaskId(3)
+                                .setDesktopTaskState(DesktopTaskState.MINIMIZED)
+                                .build())
+                        .build())
+
+            repositoryInitializer.initialize(desktopRepository)
+
+            verify(persistentRepository).readDesktop(any(), any())
+            assertThat(desktopRepository.getActiveTasks(DEFAULT_DISPLAY))
+                .containsExactly(1, 2, 3)
+                .inOrder()
+            assertThat(desktopRepository.getExpandedTasksOrdered(DEFAULT_DISPLAY))
+                .containsExactly(1, 2)
+                .inOrder()
+            assertThat(desktopRepository.getMinimizedTasks(DEFAULT_DISPLAY)).containsExactly(3)
+        }
+
+    @After
+    fun tearDown() {
+        datastoreScope.cancel()
+    }
+}
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 f95b0d1..b504a88 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
@@ -23,6 +23,8 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
 import static com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION;
+import static com.android.window.flags.Flags.FLAG_ENABLE_WINDOWING_TRANSITION_HANDLERS_OBSERVERS;
+import static com.android.window.flags.Flags.FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION;
 
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.never;
@@ -30,7 +32,9 @@
 import static org.mockito.Mockito.when;
 
 import android.app.ActivityManager;
+import android.platform.test.annotations.DisableFlags;
 import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
 import android.view.SurfaceControl;
 
 import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -49,6 +53,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.Mock;
@@ -57,14 +62,16 @@
 import java.util.Optional;
 
 /**
- * Tests for {@link FreeformTaskListener}
- * Build/Install/Run:
- * atest WMShellUnitTests:FreeformTaskListenerTests
+ * Tests for {@link FreeformTaskListener} Build/Install/Run: atest
+ * WMShellUnitTests:FreeformTaskListenerTests
  */
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public final class FreeformTaskListenerTests extends ShellTestCase {
 
+    @Rule
+    public final SetFlagsRule setFlagsRule = new SetFlagsRule();
+
     @Mock
     private ShellTaskOrganizer mTaskOrganizer;
     @Mock
@@ -79,53 +86,99 @@
     private DesktopTasksController mDesktopTasksController;
     @Mock
     private LaunchAdjacentController mLaunchAdjacentController;
+    @Mock
+    private TaskChangeListener mTaskChangeListener;
+
     private FreeformTaskListener mFreeformTaskListener;
     private StaticMockitoSession mMockitoSession;
 
     @Before
     public void setup() {
-        mMockitoSession = mockitoSession().initMocks(this)
-                .strictness(Strictness.LENIENT).mockStatic(DesktopModeStatus.class).startMocking();
+        mMockitoSession =
+                mockitoSession()
+                        .initMocks(this)
+                        .strictness(Strictness.LENIENT)
+                        .mockStatic(DesktopModeStatus.class)
+                        .startMocking();
         doReturn(true).when(() -> DesktopModeStatus.canEnterDesktopMode(any()));
 
-        mFreeformTaskListener = new FreeformTaskListener(
-                mContext,
-                mShellInit,
-                mTaskOrganizer,
-                Optional.of(mDesktopRepository),
-                Optional.of(mDesktopTasksController),
-                mLaunchAdjacentController,
-                mWindowDecorViewModel);
+        mFreeformTaskListener =
+                new FreeformTaskListener(
+                        mContext,
+                        mShellInit,
+                        mTaskOrganizer,
+                        Optional.of(mDesktopRepository),
+                        Optional.of(mDesktopTasksController),
+                        mLaunchAdjacentController,
+                        mWindowDecorViewModel,
+                        Optional.of(mTaskChangeListener));
     }
 
     @Test
-    public void testFocusTaskChanged_freeformTaskIsAddedToRepo() {
-        ActivityManager.RunningTaskInfo task = new TestRunningTaskInfoBuilder()
-                .setWindowingMode(WINDOWING_MODE_FREEFORM).build();
+    @DisableFlags(FLAG_ENABLE_WINDOWING_TRANSITION_HANDLERS_OBSERVERS)
+    public void onTaskAppeared_noTransitionObservers_visibleTask_addsTaskToRepo() {
+        ActivityManager.RunningTaskInfo task =
+                new TestRunningTaskInfoBuilder().setWindowingMode(WINDOWING_MODE_FREEFORM).build();
+        task.isVisible = true;
+
+        mFreeformTaskListener.onTaskAppeared(task, mMockSurfaceControl);
+
+        verify(mDesktopRepository).addTask(task.displayId, task.taskId, task.isVisible = true);
+    }
+
+    @Test
+    @DisableFlags(FLAG_ENABLE_WINDOWING_TRANSITION_HANDLERS_OBSERVERS)
+    public void onTaskAppeared_noTransitionObservers_nonVisibleTask_addsTaskToRepo() {
+        ActivityManager.RunningTaskInfo task =
+                new TestRunningTaskInfoBuilder().setWindowingMode(WINDOWING_MODE_FREEFORM).build();
+        task.isVisible = false;
+
+        mFreeformTaskListener.onTaskAppeared(task, mMockSurfaceControl);
+
+        verify(mDesktopRepository).addTask(task.displayId, task.taskId, task.isVisible);
+    }
+
+    @Test
+    @EnableFlags(FLAG_ENABLE_WINDOWING_TRANSITION_HANDLERS_OBSERVERS)
+    public void onTaskAppeared_useTransitionObserver_noopInRepository() {
+        ActivityManager.RunningTaskInfo task =
+                new TestRunningTaskInfoBuilder().setWindowingMode(WINDOWING_MODE_FREEFORM).build();
+        task.isVisible = true;
+
+        mFreeformTaskListener.onTaskAppeared(task, mMockSurfaceControl);
+
+        verify(mDesktopRepository, never()).addTask(task.displayId, task.taskId, task.isVisible);
+    }
+
+    @Test
+    public void focusTaskChanged_addsFreeformTaskToRepo() {
+        ActivityManager.RunningTaskInfo task =
+                new TestRunningTaskInfoBuilder().setWindowingMode(WINDOWING_MODE_FREEFORM).build();
         task.isFocused = true;
 
         mFreeformTaskListener.onFocusTaskChanged(task);
 
-        verify(mDesktopRepository)
-            .addOrMoveFreeformTaskToTop(task.displayId, task.taskId);
+        verify(mDesktopRepository).addTask(task.displayId, task.taskId, task.isVisible);
     }
 
     @Test
-    public void testFocusTaskChanged_fullscreenTaskIsNotAddedToRepo() {
-        ActivityManager.RunningTaskInfo fullscreenTask = new TestRunningTaskInfoBuilder()
-                .setWindowingMode(WINDOWING_MODE_FULLSCREEN).build();
+    public void focusTaskChanged_fullscreenTaskNotAddedToRepo() {
+        ActivityManager.RunningTaskInfo fullscreenTask =
+                new TestRunningTaskInfoBuilder()
+                        .setWindowingMode(WINDOWING_MODE_FULLSCREEN)
+                        .build();
         fullscreenTask.isFocused = true;
 
         mFreeformTaskListener.onFocusTaskChanged(fullscreenTask);
 
         verify(mDesktopRepository, never())
-                .addOrMoveFreeformTaskToTop(fullscreenTask.displayId, fullscreenTask.taskId);
+                .addTask(fullscreenTask.displayId, fullscreenTask.taskId, fullscreenTask.isVisible);
     }
 
     @Test
-    public void testVisibilityTaskChanged_visible_setLaunchAdjacentDisabled() {
-        ActivityManager.RunningTaskInfo task = new TestRunningTaskInfoBuilder()
-                .setWindowingMode(WINDOWING_MODE_FREEFORM).build();
+    public void visibilityTaskChanged_visible_setLaunchAdjacentDisabled() {
+        ActivityManager.RunningTaskInfo task =
+                new TestRunningTaskInfoBuilder().setWindowingMode(WINDOWING_MODE_FREEFORM).build();
         task.isVisible = true;
 
         mFreeformTaskListener.onTaskAppeared(task, mMockSurfaceControl);
@@ -134,9 +187,9 @@
     }
 
     @Test
-    public void testVisibilityTaskChanged_NotVisible_setLaunchAdjacentEnabled() {
-        ActivityManager.RunningTaskInfo task = new TestRunningTaskInfoBuilder()
-                .setWindowingMode(WINDOWING_MODE_FREEFORM).build();
+    public void visibilityTaskChanged_notVisible_setLaunchAdjacentEnabled() {
+        ActivityManager.RunningTaskInfo task =
+                new TestRunningTaskInfoBuilder().setWindowingMode(WINDOWING_MODE_FREEFORM).build();
         task.isVisible = true;
 
         mFreeformTaskListener.onTaskAppeared(task, mMockSurfaceControl);
@@ -149,9 +202,10 @@
 
     @Test
     @EnableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION)
-    public void onTaskVanished_nonClosingTask_isMinimized() {
-        ActivityManager.RunningTaskInfo task = new TestRunningTaskInfoBuilder()
-                .setWindowingMode(WINDOWING_MODE_FREEFORM).build();
+    @DisableFlags(FLAG_ENABLE_WINDOWING_TRANSITION_HANDLERS_OBSERVERS)
+    public void onTaskVanished_nonClosingTask_noTransitionObservers_isMinimized() {
+        ActivityManager.RunningTaskInfo task =
+                new TestRunningTaskInfoBuilder().setWindowingMode(WINDOWING_MODE_FREEFORM).build();
         task.isVisible = true;
 
         mFreeformTaskListener.onTaskAppeared(task, mMockSurfaceControl);
@@ -164,10 +218,11 @@
     }
 
     @Test
+    @DisableFlags(FLAG_ENABLE_WINDOWING_TRANSITION_HANDLERS_OBSERVERS)
     @EnableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION)
-    public void onTaskVanished_closingTask_isNotMinimized() {
-        ActivityManager.RunningTaskInfo task = new TestRunningTaskInfoBuilder()
-                .setWindowingMode(WINDOWING_MODE_FREEFORM).build();
+    public void onTaskVanished_closingTask_noTransitionObservers_isNotMinimized() {
+        ActivityManager.RunningTaskInfo task =
+                new TestRunningTaskInfoBuilder().setWindowingMode(WINDOWING_MODE_FREEFORM).build();
         task.isVisible = true;
 
         mFreeformTaskListener.onTaskAppeared(task, mMockSurfaceControl);
@@ -183,9 +238,23 @@
     }
 
     @Test
+    @EnableFlags(FLAG_ENABLE_WINDOWING_TRANSITION_HANDLERS_OBSERVERS)
+    public void onTaskVanished_usesTransitionObservers_noopInRepo() {
+        ActivityManager.RunningTaskInfo task =
+                new TestRunningTaskInfoBuilder().setWindowingMode(WINDOWING_MODE_FREEFORM).build();
+        mFreeformTaskListener.onTaskAppeared(task, mMockSurfaceControl);
+
+        mFreeformTaskListener.onTaskVanished(task);
+
+        verify(mDesktopRepository, never()).minimizeTask(task.displayId, task.taskId);
+        verify(mDesktopRepository, never()).removeClosingTask(task.taskId);
+        verify(mDesktopRepository, never()).removeFreeformTask(task.displayId, task.taskId);
+    }
+
+    @Test
     public void onTaskInfoChanged_withDesktopController_forwards() {
-        ActivityManager.RunningTaskInfo task = new TestRunningTaskInfoBuilder()
-                .setWindowingMode(WINDOWING_MODE_FREEFORM).build();
+        ActivityManager.RunningTaskInfo task =
+                new TestRunningTaskInfoBuilder().setWindowingMode(WINDOWING_MODE_FREEFORM).build();
         task.isVisible = true;
         mFreeformTaskListener.onTaskAppeared(task, mMockSurfaceControl);
 
@@ -194,6 +263,36 @@
         verify(mDesktopTasksController).onTaskInfoChanged(task);
     }
 
+    @Test
+    @DisableFlags(FLAG_ENABLE_WINDOWING_TRANSITION_HANDLERS_OBSERVERS)
+    public void onTaskInfoChanged_noTransitionObservers_updatesTask() {
+        ActivityManager.RunningTaskInfo task =
+                new TestRunningTaskInfoBuilder().setWindowingMode(WINDOWING_MODE_FREEFORM).build();
+        task.isVisible = true;
+        mFreeformTaskListener.onTaskAppeared(task, mMockSurfaceControl);
+
+        mFreeformTaskListener.onTaskInfoChanged(task);
+
+        verify(mTaskChangeListener, never()).onTaskChanging(any());
+        verify(mDesktopRepository).updateTask(task.displayId, task.taskId, task.isVisible);
+    }
+
+    @Test
+    @EnableFlags(FLAG_ENABLE_WINDOWING_TRANSITION_HANDLERS_OBSERVERS)
+    @DisableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION)
+    public void onTaskInfoChanged_useTransitionObserver_noopInRepository() {
+        ActivityManager.RunningTaskInfo task =
+                new TestRunningTaskInfoBuilder().setWindowingMode(WINDOWING_MODE_FREEFORM).build();
+        task.isVisible = true;
+        mFreeformTaskListener.onTaskAppeared(task, mMockSurfaceControl);
+
+        mFreeformTaskListener.onTaskInfoChanged(task);
+
+        verify(mTaskChangeListener).onNonTransitionTaskChanging(any());
+        verify(mDesktopRepository, never())
+                .updateTask(task.displayId, task.taskId, task.isVisible);
+    }
+
     @After
     public void tearDown() {
         mMockitoSession.finishMocking();
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserverTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserverTest.java
index 90ab2b8..5aed461 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserverTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserverTest.java
@@ -58,25 +58,17 @@
 
 import java.util.Optional;
 
-/**
- * Tests for {@link FreeformTaskTransitionObserver}.
- */
+/** Tests for {@link FreeformTaskTransitionObserver}. */
 @SmallTest
 public class FreeformTaskTransitionObserverTest {
 
     public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
-    @Mock
-    private ShellInit mShellInit;
-    @Mock
-    private Transitions mTransitions;
-    @Mock
-    private DesktopImmersiveController mDesktopImmersiveController;
-    @Mock
-    private WindowDecorViewModel mWindowDecorViewModel;
-    @Mock
-    private TaskChangeListener mTaskChangeListener;
-    @Mock
-    private FocusTransitionObserver mFocusTransitionObserver;
+    @Mock private ShellInit mShellInit;
+    @Mock private Transitions mTransitions;
+    @Mock private DesktopImmersiveController mDesktopImmersiveController;
+    @Mock private WindowDecorViewModel mWindowDecorViewModel;
+    @Mock private TaskChangeListener mTaskChangeListener;
+    @Mock private FocusTransitionObserver mFocusTransitionObserver;
 
     private FreeformTaskTransitionObserver mTransitionObserver;
 
@@ -85,20 +77,22 @@
         MockitoAnnotations.initMocks(this);
 
         PackageManager pm = mock(PackageManager.class);
-        doReturn(true).when(pm).hasSystemFeature(
-            PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT);
+        doReturn(true).when(pm).hasSystemFeature(PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT);
         final Context context = mock(Context.class);
         doReturn(pm).when(context).getPackageManager();
 
-        mTransitionObserver = new FreeformTaskTransitionObserver(
-                context, mShellInit, mTransitions,
-                Optional.of(mDesktopImmersiveController),
-                mWindowDecorViewModel, Optional.of(mTaskChangeListener), mFocusTransitionObserver);
+        mTransitionObserver =
+                new FreeformTaskTransitionObserver(
+                        context,
+                        mShellInit,
+                        mTransitions,
+                        Optional.of(mDesktopImmersiveController),
+                        mWindowDecorViewModel,
+                        Optional.of(mTaskChangeListener),
+                        mFocusTransitionObserver);
 
-        final ArgumentCaptor<Runnable> initRunnableCaptor = ArgumentCaptor.forClass(
-                Runnable.class);
-        verify(mShellInit).addInitCallback(initRunnableCaptor.capture(),
-                same(mTransitionObserver));
+        final ArgumentCaptor<Runnable> initRunnableCaptor = ArgumentCaptor.forClass(Runnable.class);
+        verify(mShellInit).addInitCallback(initRunnableCaptor.capture(), same(mTransitionObserver));
         initRunnableCaptor.getValue().run();
     }
 
@@ -109,10 +103,9 @@
 
     @Test
     public void openTransition_createsWindowDecor() {
-        final TransitionInfo.Change change =
-                createChange(TRANSIT_OPEN, 1, WINDOWING_MODE_FREEFORM);
-        final TransitionInfo info = new TransitionInfoBuilder(TRANSIT_OPEN, 0)
-                .addChange(change).build();
+        final TransitionInfo.Change change = createChange(TRANSIT_OPEN, 1, WINDOWING_MODE_FREEFORM);
+        final TransitionInfo info =
+                new TransitionInfoBuilder(TRANSIT_OPEN, 0).addChange(change).build();
 
         final IBinder transition = mock(IBinder.class);
         final SurfaceControl.Transaction startT = mock(SurfaceControl.Transaction.class);
@@ -120,16 +113,15 @@
         mTransitionObserver.onTransitionReady(transition, info, startT, finishT);
         mTransitionObserver.onTransitionStarting(transition);
 
-        verify(mWindowDecorViewModel).onTaskOpening(
-                change.getTaskInfo(), change.getLeash(), startT, finishT);
+        verify(mWindowDecorViewModel)
+                .onTaskOpening(change.getTaskInfo(), change.getLeash(), startT, finishT);
     }
 
     @Test
     public void openTransition_notifiesOnTaskOpening() {
-        final TransitionInfo.Change change =
-                createChange(TRANSIT_OPEN, 1, WINDOWING_MODE_FREEFORM);
-        final TransitionInfo info = new TransitionInfoBuilder(TRANSIT_OPEN, 0)
-                .addChange(change).build();
+        final TransitionInfo.Change change = createChange(TRANSIT_OPEN, 1, WINDOWING_MODE_FREEFORM);
+        final TransitionInfo info =
+                new TransitionInfoBuilder(TRANSIT_OPEN, 0).addChange(change).build();
 
         final IBinder transition = mock(IBinder.class);
         final SurfaceControl.Transaction startT = mock(SurfaceControl.Transaction.class);
@@ -144,8 +136,10 @@
     public void toFrontTransition_notifiesOnTaskMovingToFront() {
         final TransitionInfo.Change change =
                 createChange(TRANSIT_TO_FRONT, /* taskId= */ 1, WINDOWING_MODE_FREEFORM);
-        final TransitionInfo info = new TransitionInfoBuilder(TRANSIT_TO_FRONT, /* flags= */ 0)
-                .addChange(change).build();
+        final TransitionInfo info =
+                new TransitionInfoBuilder(TRANSIT_TO_FRONT, /* flags= */ 0)
+                        .addChange(change)
+                        .build();
 
         final IBinder transition = mock(IBinder.class);
         final SurfaceControl.Transaction startT = mock(SurfaceControl.Transaction.class);
@@ -160,8 +154,10 @@
     public void toBackTransition_notifiesOnTaskMovingToBack() {
         final TransitionInfo.Change change =
                 createChange(TRANSIT_TO_BACK, /* taskId= */ 1, WINDOWING_MODE_FREEFORM);
-        final TransitionInfo info = new TransitionInfoBuilder(TRANSIT_TO_BACK, /* flags= */ 0)
-                .addChange(change).build();
+        final TransitionInfo info =
+                new TransitionInfoBuilder(TRANSIT_TO_BACK, /* flags= */ 0)
+                        .addChange(change)
+                        .build();
 
         final IBinder transition = mock(IBinder.class);
         final SurfaceControl.Transaction startT = mock(SurfaceControl.Transaction.class);
@@ -173,11 +169,11 @@
     }
 
     @Test
-    public void changeTransition_notifiesOnTaskChanging() {
+    public void changeTransition_notifiesOnTaskChange() {
         final TransitionInfo.Change change =
                 createChange(TRANSIT_CHANGE, /* taskId= */ 1, WINDOWING_MODE_FREEFORM);
-        final TransitionInfo info = new TransitionInfoBuilder(TRANSIT_CHANGE, /* flags= */ 0)
-                .addChange(change).build();
+        final TransitionInfo info =
+                new TransitionInfoBuilder(TRANSIT_CHANGE, /* flags= */ 0).addChange(change).build();
 
         final IBinder transition = mock(IBinder.class);
         final SurfaceControl.Transaction startT = mock(SurfaceControl.Transaction.class);
@@ -192,8 +188,8 @@
     public void closeTransition_preparesWindowDecor() {
         final TransitionInfo.Change change =
                 createChange(TRANSIT_CLOSE, 1, WINDOWING_MODE_FREEFORM);
-        final TransitionInfo info = new TransitionInfoBuilder(TRANSIT_CLOSE, 0)
-                .addChange(change).build();
+        final TransitionInfo info =
+                new TransitionInfoBuilder(TRANSIT_CLOSE, 0).addChange(change).build();
 
         final IBinder transition = mock(IBinder.class);
         final SurfaceControl.Transaction startT = mock(SurfaceControl.Transaction.class);
@@ -201,16 +197,15 @@
         mTransitionObserver.onTransitionReady(transition, info, startT, finishT);
         mTransitionObserver.onTransitionStarting(transition);
 
-        verify(mWindowDecorViewModel).onTaskClosing(
-                change.getTaskInfo(), startT, finishT);
+        verify(mWindowDecorViewModel).onTaskClosing(change.getTaskInfo(), startT, finishT);
     }
 
     @Test
     public void closeTransition_notifiesOnTaskClosing() {
         final TransitionInfo.Change change =
                 createChange(TRANSIT_CLOSE, 1, WINDOWING_MODE_FREEFORM);
-        final TransitionInfo info = new TransitionInfoBuilder(TRANSIT_CLOSE, 0)
-                .addChange(change).build();
+        final TransitionInfo info =
+                new TransitionInfoBuilder(TRANSIT_CLOSE, 0).addChange(change).build();
 
         final IBinder transition = mock(IBinder.class);
         final SurfaceControl.Transaction startT = mock(SurfaceControl.Transaction.class);
@@ -225,8 +220,8 @@
     public void closeTransition_doesntCloseWindowDecorDuringTransition() throws Exception {
         final TransitionInfo.Change change =
                 createChange(TRANSIT_CLOSE, 1, WINDOWING_MODE_FREEFORM);
-        final TransitionInfo info = new TransitionInfoBuilder(TRANSIT_CLOSE, 0)
-                .addChange(change).build();
+        final TransitionInfo info =
+                new TransitionInfoBuilder(TRANSIT_CLOSE, 0).addChange(change).build();
 
         final IBinder transition = mock(IBinder.class);
         final SurfaceControl.Transaction startT = mock(SurfaceControl.Transaction.class);
@@ -241,8 +236,8 @@
     public void closeTransition_closesWindowDecorAfterTransition() throws Exception {
         final TransitionInfo.Change change =
                 createChange(TRANSIT_CLOSE, 1, WINDOWING_MODE_FREEFORM);
-        final TransitionInfo info = new TransitionInfoBuilder(TRANSIT_CLOSE, 0)
-                .addChange(change).build();
+        final TransitionInfo info =
+                new TransitionInfoBuilder(TRANSIT_CLOSE, 0).addChange(change).build();
 
         final AutoCloseable windowDecor = mock(AutoCloseable.class);
 
@@ -261,8 +256,8 @@
         // The playing transition
         final TransitionInfo.Change change1 =
                 createChange(TRANSIT_OPEN, 1, WINDOWING_MODE_FREEFORM);
-        final TransitionInfo info1 = new TransitionInfoBuilder(TRANSIT_OPEN, 0)
-                .addChange(change1).build();
+        final TransitionInfo info1 =
+                new TransitionInfoBuilder(TRANSIT_OPEN, 0).addChange(change1).build();
 
         final IBinder transition1 = mock(IBinder.class);
         final SurfaceControl.Transaction startT1 = mock(SurfaceControl.Transaction.class);
@@ -273,8 +268,8 @@
         // The merged transition
         final TransitionInfo.Change change2 =
                 createChange(TRANSIT_CLOSE, 2, WINDOWING_MODE_FREEFORM);
-        final TransitionInfo info2 = new TransitionInfoBuilder(TRANSIT_CLOSE, 0)
-                .addChange(change2).build();
+        final TransitionInfo info2 =
+                new TransitionInfoBuilder(TRANSIT_CLOSE, 0).addChange(change2).build();
 
         final IBinder transition2 = mock(IBinder.class);
         final SurfaceControl.Transaction startT2 = mock(SurfaceControl.Transaction.class);
@@ -292,8 +287,8 @@
         // The playing transition
         final TransitionInfo.Change change1 =
                 createChange(TRANSIT_CLOSE, 1, WINDOWING_MODE_FREEFORM);
-        final TransitionInfo info1 = new TransitionInfoBuilder(TRANSIT_CLOSE, 0)
-                .addChange(change1).build();
+        final TransitionInfo info1 =
+                new TransitionInfoBuilder(TRANSIT_CLOSE, 0).addChange(change1).build();
 
         final IBinder transition1 = mock(IBinder.class);
         final SurfaceControl.Transaction startT1 = mock(SurfaceControl.Transaction.class);
@@ -304,8 +299,8 @@
         // The merged transition
         final TransitionInfo.Change change2 =
                 createChange(TRANSIT_CLOSE, 2, WINDOWING_MODE_FREEFORM);
-        final TransitionInfo info2 = new TransitionInfoBuilder(TRANSIT_CLOSE, 0)
-                .addChange(change2).build();
+        final TransitionInfo info2 =
+                new TransitionInfoBuilder(TRANSIT_CLOSE, 0).addChange(change2).build();
 
         final IBinder transition2 = mock(IBinder.class);
         final SurfaceControl.Transaction startT2 = mock(SurfaceControl.Transaction.class);
@@ -368,9 +363,10 @@
         taskInfo.taskId = taskId;
         taskInfo.configuration.windowConfiguration.setWindowingMode(windowingMode);
 
-        final TransitionInfo.Change change = new TransitionInfo.Change(
-                new WindowContainerToken(mock(IWindowContainerToken.class)),
-                mock(SurfaceControl.class));
+        final TransitionInfo.Change change =
+                new TransitionInfo.Change(
+                        new WindowContainerToken(mock(IWindowContainerToken.class)),
+                        mock(SurfaceControl.class));
         change.setMode(mode);
         change.setTaskInfo(taskInfo);
         return change;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipResizeGestureHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipResizeGestureHandlerTest.java
index 66f8c0b..880ca2c 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipResizeGestureHandlerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipResizeGestureHandlerTest.java
@@ -124,7 +124,7 @@
         mPipResizeGestureHandler = new PipResizeGestureHandler(mContext, pipBoundsAlgorithm,
                 mPipBoundsState, motionHelper, mPipTouchState, mPipTaskOrganizer,
                 mPipDismissTargetHandler,
-                () -> {}, mPipUiEventLogger, mPhonePipMenuController,
+                (Rect bounds) -> new Rect(), () -> {}, mPipUiEventLogger, mPhonePipMenuController,
                 mMainExecutor, null /* pipPerfHintController */) {
             @Override
             public void pilferPointers() {
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipAlphaAnimatorTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipAlphaAnimatorTest.java
new file mode 100644
index 0000000..9cc18ff
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipAlphaAnimatorTest.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.pip2.animation;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyFloat;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.view.SurfaceControl;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.wm.shell.pip2.PipSurfaceTransactionHelper;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Unit test against {@link PipAlphaAnimator}.
+ */
+@SmallTest
+@TestableLooper.RunWithLooper
+@RunWith(AndroidTestingRunner.class)
+public class PipAlphaAnimatorTest {
+
+    @Mock private Context mMockContext;
+
+    @Mock private Resources mMockResources;
+
+    @Mock private PipSurfaceTransactionHelper.SurfaceControlTransactionFactory mMockFactory;
+
+    @Mock private SurfaceControl.Transaction mMockTransaction;
+
+    @Mock private Runnable mMockStartCallback;
+
+    @Mock private Runnable mMockEndCallback;
+
+    private PipAlphaAnimator mPipAlphaAnimator;
+    private SurfaceControl mTestLeash;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        when(mMockContext.getResources()).thenReturn(mMockResources);
+        when(mMockResources.getInteger(anyInt())).thenReturn(0);
+        when(mMockFactory.getTransaction()).thenReturn(mMockTransaction);
+        when(mMockTransaction.setAlpha(any(SurfaceControl.class), anyFloat()))
+                .thenReturn(mMockTransaction);
+
+        mTestLeash = new SurfaceControl.Builder()
+                .setContainerLayer()
+                .setName("PipAlphaAnimatorTest")
+                .setCallsite("PipAlphaAnimatorTest")
+                .build();
+    }
+
+    @Test
+    public void setAnimationStartCallback_fadeInAnimator_callbackStartCallback() {
+        mPipAlphaAnimator = new PipAlphaAnimator(mMockContext, mTestLeash, mMockTransaction,
+                PipAlphaAnimator.FADE_IN);
+
+        mPipAlphaAnimator.setAnimationStartCallback(mMockStartCallback);
+        mPipAlphaAnimator.setAnimationEndCallback(mMockEndCallback);
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+            mPipAlphaAnimator.start();
+            mPipAlphaAnimator.pause();
+        });
+
+        verify(mMockStartCallback).run();
+        verifyZeroInteractions(mMockEndCallback);
+    }
+
+    @Test
+    public void setAnimationEndCallback_fadeInAnimator_callbackStartAndEndCallback() {
+        mPipAlphaAnimator = new PipAlphaAnimator(mMockContext, mTestLeash, mMockTransaction,
+                PipAlphaAnimator.FADE_IN);
+
+        mPipAlphaAnimator.setAnimationStartCallback(mMockStartCallback);
+        mPipAlphaAnimator.setAnimationEndCallback(mMockEndCallback);
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+            mPipAlphaAnimator.start();
+            mPipAlphaAnimator.end();
+        });
+
+        verify(mMockStartCallback).run();
+        verify(mMockStartCallback).run();
+    }
+
+    @Test
+    public void onAnimationEnd_fadeInAnimator_leashVisibleAtEnd() {
+        mPipAlphaAnimator = new PipAlphaAnimator(mMockContext, mTestLeash, mMockTransaction,
+                PipAlphaAnimator.FADE_IN);
+        mPipAlphaAnimator.setSurfaceControlTransactionFactory(mMockFactory);
+
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+            mPipAlphaAnimator.start();
+            clearInvocations(mMockTransaction);
+            mPipAlphaAnimator.end();
+        });
+
+        verify(mMockTransaction).setAlpha(mTestLeash, 1.0f);
+    }
+
+    @Test
+    public void onAnimationEnd_fadeOutAnimator_leashInvisibleAtEnd() {
+        mPipAlphaAnimator = new PipAlphaAnimator(mMockContext, mTestLeash, mMockTransaction,
+                PipAlphaAnimator.FADE_OUT);
+        mPipAlphaAnimator.setSurfaceControlTransactionFactory(mMockFactory);
+
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+            mPipAlphaAnimator.start();
+            clearInvocations(mMockTransaction);
+            mPipAlphaAnimator.end();
+        });
+
+        verify(mMockTransaction).setAlpha(mTestLeash, 0f);
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/animation/WindowAnimatorTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/animation/WindowAnimatorTest.kt
new file mode 100644
index 0000000..c19232b
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/animation/WindowAnimatorTest.kt
@@ -0,0 +1,198 @@
+/*
+ * 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.shared.animation
+
+import android.graphics.PointF
+import android.graphics.Rect
+import android.util.DisplayMetrics
+import android.view.SurfaceControl
+import android.window.TransitionInfo
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import androidx.test.internal.runner.junit4.statement.UiThreadStatement.runOnUiThread
+import com.android.app.animation.Interpolators
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.any
+import org.mockito.ArgumentMatchers.anyFloat
+import org.mockito.kotlin.argumentCaptor
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.whenever
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class WindowAnimatorTest {
+
+    private val transaction = mock<SurfaceControl.Transaction>()
+    private val change = mock<TransitionInfo.Change>()
+    private val leash = mock<SurfaceControl>()
+
+    private val displayMetrics = DisplayMetrics().apply { density = 1f }
+
+    private val positionXArgumentCaptor = argumentCaptor<Float>()
+    private val positionYArgumentCaptor = argumentCaptor<Float>()
+    private val scaleXArgumentCaptor = argumentCaptor<Float>()
+    private val scaleYArgumentCaptor = argumentCaptor<Float>()
+
+    @Before
+    fun setup() {
+        whenever(change.leash).thenReturn(leash)
+        whenever(change.endAbsBounds).thenReturn(END_BOUNDS)
+        whenever(transaction.setPosition(any(), anyFloat(), anyFloat())).thenReturn(transaction)
+        whenever(transaction.setScale(any(), anyFloat(), anyFloat())).thenReturn(transaction)
+        whenever(
+            transaction.setPosition(
+                any(),
+                positionXArgumentCaptor.capture(),
+                positionYArgumentCaptor.capture(),
+            )
+        )
+            .thenReturn(transaction)
+        whenever(
+            transaction.setScale(
+                any(),
+                scaleXArgumentCaptor.capture(),
+                scaleYArgumentCaptor.capture(),
+            )
+        )
+            .thenReturn(transaction)
+    }
+
+    @Test
+    fun createBoundsAnimator_returnsCorrectDefaultAnimatorParams() = runOnUiThread {
+        val boundsAnimParams =
+            WindowAnimator.BoundsAnimationParams(
+                durationMs = 100L,
+                interpolator = Interpolators.STANDARD_ACCELERATE,
+            )
+
+        val valueAnimator =
+            WindowAnimator.createBoundsAnimator(
+                displayMetrics,
+                boundsAnimParams,
+                change,
+                transaction
+            )
+        valueAnimator.start()
+
+        assertThat(valueAnimator.duration).isEqualTo(100L)
+        assertThat(valueAnimator.interpolator).isEqualTo(Interpolators.STANDARD_ACCELERATE)
+        val expectedPosition = PointF(END_BOUNDS.left.toFloat(), END_BOUNDS.top.toFloat())
+        assertTransactionParams(expectedPosition, expectedScale = PointF(1f, 1f))
+    }
+
+    @Test
+    fun createBoundsAnimator_startScaleAndOffset_correctPosAndScale() = runOnUiThread {
+        val bounds = Rect(/* left= */ 100, /* top= */ 200, /* right= */ 300, /* bottom= */ 400)
+        whenever(change.endAbsBounds).thenReturn(bounds)
+        val boundsAnimParams =
+            WindowAnimator.BoundsAnimationParams(
+                durationMs = 100L,
+                startOffsetYDp = 10f,
+                startScale = 0.5f,
+                interpolator = Interpolators.STANDARD_ACCELERATE,
+            )
+
+        val valueAnimator =
+            WindowAnimator.createBoundsAnimator(
+                displayMetrics,
+                boundsAnimParams,
+                change,
+                transaction
+            )
+        valueAnimator.start()
+
+        assertTransactionParams(
+            expectedPosition = PointF(150f, 260f),
+            expectedScale = PointF(0.5f, 0.5f),
+        )
+    }
+
+    @Test
+    fun createBoundsAnimator_endScaleAndOffset_correctPosAndScale() = runOnUiThread {
+        val bounds = Rect(/* left= */ 100, /* top= */ 200, /* right= */ 300, /* bottom= */ 400)
+        whenever(change.endAbsBounds).thenReturn(bounds)
+        val boundsAnimParams =
+            WindowAnimator.BoundsAnimationParams(
+                durationMs = 100L,
+                endOffsetYDp = 10f,
+                endScale = 0.5f,
+                interpolator = Interpolators.STANDARD_ACCELERATE,
+            )
+
+        val valueAnimator =
+            WindowAnimator.createBoundsAnimator(
+                displayMetrics,
+                boundsAnimParams,
+                change,
+                transaction
+            )
+        valueAnimator.start()
+        valueAnimator.end()
+
+        assertTransactionParams(
+            expectedPosition = PointF(150f, 260f),
+            expectedScale = PointF(0.5f, 0.5f),
+        )
+    }
+
+    @Test
+    fun createBoundsAnimator_middleOfAnimation_correctPosAndScale() = runOnUiThread {
+        val bounds = Rect(/* left= */ 100, /* top= */ 200, /* right= */ 300, /* bottom= */ 400)
+        whenever(change.endAbsBounds).thenReturn(bounds)
+        val boundsAnimParams =
+            WindowAnimator.BoundsAnimationParams(
+                durationMs = 100L,
+                endOffsetYDp = 10f,
+                startScale = 0.5f,
+                endScale = 0.9f,
+                interpolator = Interpolators.LINEAR,
+            )
+
+        val valueAnimator =
+            WindowAnimator.createBoundsAnimator(
+                displayMetrics,
+                boundsAnimParams,
+                change,
+                transaction
+            )
+        valueAnimator.currentPlayTime = 50
+
+        assertTransactionParams(
+            // We should have a window of size 140x140, which we centre by placing at pos 130, 230.
+            // Then add 10*0.5 as y-offset
+            expectedPosition = PointF(130f, 235f),
+            expectedScale = PointF(0.7f, 0.7f),
+        )
+    }
+
+    private fun assertTransactionParams(expectedPosition: PointF, expectedScale: PointF) {
+        assertThat(positionXArgumentCaptor.lastValue).isWithin(TOLERANCE).of(expectedPosition.x)
+        assertThat(positionYArgumentCaptor.lastValue).isWithin(TOLERANCE).of(expectedPosition.y)
+        assertThat(scaleXArgumentCaptor.lastValue).isWithin(TOLERANCE).of(expectedScale.x)
+        assertThat(scaleYArgumentCaptor.lastValue).isWithin(TOLERANCE).of(expectedScale.y)
+    }
+
+    companion object {
+        private val END_BOUNDS =
+            Rect(/* left= */ 10, /* top= */ 20, /* right= */ 30, /* bottom= */ 40)
+
+        private const val TOLERANCE = 1e-3f
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTransitionsTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTransitionsTest.java
index d3e40f2..60e2030 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTransitionsTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTransitionsTest.java
@@ -33,7 +33,8 @@
 import android.app.ActivityManager;
 import android.graphics.Rect;
 import android.os.IBinder;
-import android.testing.AndroidTestingRunner;
+import android.platform.test.flag.junit.FlagsParameterization;
+import android.platform.test.flag.junit.SetFlagsRule;
 import android.testing.TestableLooper;
 import android.view.SurfaceControl;
 import android.window.TransitionInfo;
@@ -42,10 +43,12 @@
 
 import androidx.test.filters.SmallTest;
 
+import com.android.wm.shell.Flags;
 import com.android.wm.shell.ShellTestCase;
 import com.android.wm.shell.transition.Transitions;
 
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
@@ -54,11 +57,23 @@
 import java.util.ArrayList;
 import java.util.List;
 
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4;
+import platform.test.runner.parameterized.Parameters;
+
 @SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(ParameterizedAndroidJunit4.class)
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
 public class TaskViewTransitionsTest extends ShellTestCase {
 
+    @Parameters(name = "{0}")
+    public static List<FlagsParameterization> getParams() {
+        return FlagsParameterization.allCombinationsOf(
+                Flags.FLAG_ENABLE_TASK_VIEW_CONTROLLER_CLEANUP);
+    }
+
+    @Rule
+    public final SetFlagsRule mSetFlagsRule;
+
     @Mock
     Transitions mTransitions;
     @Mock
@@ -70,6 +85,10 @@
 
     TaskViewTransitions mTaskViewTransitions;
 
+    public TaskViewTransitionsTest(FlagsParameterization flags) {
+        mSetFlagsRule = new SetFlagsRule(flags);
+    }
+
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopHeaderManageWindowsMenuTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopHeaderManageWindowsMenuTest.kt
index f9f760e..1215c52 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopHeaderManageWindowsMenuTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopHeaderManageWindowsMenuTest.kt
@@ -64,13 +64,14 @@
             context = context,
             shellInit = ShellInit(TestShellExecutor()),
             persistentRepository = mock(),
+            repositoryInitializer = mock(),
             mainCoroutineScope = mock()
         )
     }
 
     @After
     fun tearDown() {
-        menu.close()
+        menu.animateClose()
     }
 
     @Test
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 1839b8a..c42be7f 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
@@ -127,6 +127,7 @@
 import org.mockito.Mockito.mock
 import org.mockito.Mockito.never
 import org.mockito.Mockito.times
+import org.mockito.kotlin.KArgumentCaptor
 import org.mockito.kotlin.verify
 import org.mockito.kotlin.any
 import org.mockito.kotlin.argThat
@@ -665,7 +666,8 @@
             eq(currentBounds),
             eq(SnapPosition.LEFT),
             eq(ResizeTrigger.SNAP_LEFT_MENU),
-            eq(null)
+            eq(null),
+            eq(decor)
         )
         assertEquals(taskSurfaceCaptor.firstValue, decor.mTaskSurface)
     }
@@ -705,7 +707,8 @@
             eq(currentBounds),
             eq(SnapPosition.LEFT),
             eq(ResizeTrigger.SNAP_LEFT_MENU),
-            eq(null)
+            eq(null),
+            eq(decor),
         )
         assertEquals(decor.mTaskSurface, taskSurfaceCaptor.firstValue)
     }
@@ -726,7 +729,9 @@
         verify(mockDesktopTasksController, never())
             .snapToHalfScreen(eq(decor.mTaskInfo), any(), eq(currentBounds), eq(SnapPosition.LEFT),
                 eq(ResizeTrigger.MAXIMIZE_BUTTON),
-                eq(null))
+                eq(null),
+                eq(decor),
+            )
         verify(mockToast).show()
     }
 
@@ -749,7 +754,8 @@
             eq(currentBounds),
             eq(SnapPosition.RIGHT),
             eq(ResizeTrigger.SNAP_RIGHT_MENU),
-            eq(null)
+            eq(null),
+            eq(decor),
         )
         assertEquals(decor.mTaskSurface, taskSurfaceCaptor.firstValue)
     }
@@ -789,7 +795,8 @@
             eq(currentBounds),
             eq(SnapPosition.RIGHT),
             eq(ResizeTrigger.SNAP_RIGHT_MENU),
-            eq(null)
+            eq(null),
+            eq(decor),
         )
         assertEquals(decor.mTaskSurface, taskSurfaceCaptor.firstValue)
     }
@@ -810,7 +817,9 @@
         verify(mockDesktopTasksController, never())
             .snapToHalfScreen(eq(decor.mTaskInfo), any(), eq(currentBounds), eq(SnapPosition.RIGHT),
                 eq(ResizeTrigger.MAXIMIZE_BUTTON),
-                eq(null))
+                eq(null),
+                eq(decor),
+        )
         verify(mockToast).show()
     }
 
@@ -937,7 +946,7 @@
     }
 
     @Test
-    fun testDecor_onClickToSplitScreen_disposesStatusBarInputLayer() {
+    fun testDecor_onClickToSplitScreen_detachesStatusBarInputLayer() {
         val toSplitScreenListenerCaptor = forClass(Function0::class.java)
                 as ArgumentCaptor<Function0<Unit>>
         val decor = createOpenTaskDecoration(
@@ -947,7 +956,7 @@
 
         toSplitScreenListenerCaptor.value.invoke()
 
-        verify(decor).disposeStatusBarInputLayer()
+        verify(decor).detachStatusBarInputLayer()
     }
 
     @Test
@@ -1278,12 +1287,45 @@
             .toggleDesktopTaskSize(decor.mTaskInfo, ResizeTrigger.MAXIMIZE_BUTTON, null)
     }
 
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP)
+    fun testImmersiveClick_togglesImmersiveState() {
+        val onImmersiveClickCaptor = argumentCaptor<() -> Unit>()
+        val decor = createOpenTaskDecoration(
+            windowingMode = WINDOWING_MODE_FREEFORM,
+            onImmersiveOrRestoreListenerCaptor = onImmersiveClickCaptor,
+            requestingImmersive = true,
+        )
+
+        onImmersiveClickCaptor.firstValue()
+
+        verify(mockDesktopTasksController)
+            .toggleDesktopTaskFullImmersiveState(decor.mTaskInfo)
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP)
+    fun testImmersiveClick_closesMaximizeMenu() {
+        val onImmersiveClickCaptor = argumentCaptor<() -> Unit>()
+        val decor = createOpenTaskDecoration(
+            windowingMode = WINDOWING_MODE_FREEFORM,
+            onImmersiveOrRestoreListenerCaptor = onImmersiveClickCaptor,
+            requestingImmersive = true,
+        )
+
+        onImmersiveClickCaptor.firstValue()
+
+        verify(decor).closeMaximizeMenu()
+    }
+
     private fun createOpenTaskDecoration(
         @WindowingMode windowingMode: Int,
         taskSurface: SurfaceControl = SurfaceControl(),
         requestingImmersive: Boolean = false,
         onMaxOrRestoreListenerCaptor: ArgumentCaptor<Function0<Unit>> =
             forClass(Function0::class.java) as ArgumentCaptor<Function0<Unit>>,
+        onImmersiveOrRestoreListenerCaptor: KArgumentCaptor<() -> Unit> =
+            argumentCaptor<() -> Unit>(),
         onLeftSnapClickListenerCaptor: ArgumentCaptor<Function0<Unit>> =
             forClass(Function0::class.java) as ArgumentCaptor<Function0<Unit>>,
         onRightSnapClickListenerCaptor: ArgumentCaptor<Function0<Unit>> =
@@ -1307,6 +1349,8 @@
         ))
         onTaskOpening(decor.mTaskInfo, taskSurface)
         verify(decor).setOnMaximizeOrRestoreClickListener(onMaxOrRestoreListenerCaptor.capture())
+        verify(decor)
+            .setOnImmersiveOrRestoreClickListener(onImmersiveOrRestoreListenerCaptor.capture())
         verify(decor).setOnLeftSnapClickListener(onLeftSnapClickListenerCaptor.capture())
         verify(decor).setOnRightSnapClickListener(onRightSnapClickListenerCaptor.capture())
         verify(decor).setOnToDesktopClickListener(onToDesktopClickListenerCaptor.capture())
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java
index 0afb6c1..8a2c778 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java
@@ -49,7 +49,6 @@
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
-import static org.mockito.kotlin.VerificationKt.times;
 
 import android.app.ActivityManager;
 import android.app.assist.AssistContent;
@@ -850,8 +849,7 @@
         ArgumentCaptor<Runnable> runnableArgument = ArgumentCaptor.forClass(Runnable.class);
         spyWindowDecor.relayout(taskInfo, true /* hasGlobalFocus */);
 
-        // Once for view host, the other for the AppHandle input layer.
-        verify(mMockHandler, times(2)).post(runnableArgument.capture());
+        verify(mMockHandler).post(runnableArgument.capture());
         runnableArgument.getValue().run();
         verify(mMockSurfaceControlViewHostFactory).create(any(), any(), any());
     }
@@ -878,8 +876,7 @@
         taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
         ArgumentCaptor<Runnable> runnableArgument = ArgumentCaptor.forClass(Runnable.class);
         spyWindowDecor.relayout(taskInfo, true /* hasGlobalFocus */);
-        // Once for view host, the other for the AppHandle input layer.
-        verify(mMockHandler, times(2)).post(runnableArgument.capture());
+        verify(mMockHandler).post(runnableArgument.capture());
 
         spyWindowDecor.relayout(taskInfo, true /* hasGlobalFocus */);
 
@@ -893,8 +890,7 @@
         taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
         ArgumentCaptor<Runnable> runnableArgument = ArgumentCaptor.forClass(Runnable.class);
         spyWindowDecor.relayout(taskInfo, true /* hasGlobalFocus */);
-        // Once for view host, the other for the AppHandle input layer.
-        verify(mMockHandler, times(2)).post(runnableArgument.capture());
+        verify(mMockHandler).post(runnableArgument.capture());
 
         spyWindowDecor.close();
 
@@ -909,8 +905,10 @@
                 new FakeMaximizeMenuFactory(menu));
         assertFalse(decoration.isMaximizeMenuActive());
 
-        createMaximizeMenu(decoration, menu);
+        createMaximizeMenu(decoration);
 
+        verify(menu).show(anyBoolean(), anyInt(), anyBoolean(), anyBoolean(), any(), any(), any(),
+                any(), mOnMaxMenuHoverChangeListener.capture(), any());
         assertTrue(decoration.isMaximizeMenuActive());
     }
 
@@ -921,7 +919,9 @@
         final DesktopModeWindowDecoration decoration = createWindowDecoration(taskInfo,
                 new FakeMaximizeMenuFactory(menu));
         decoration.setAppHeaderMaximizeButtonHovered(false);
-        createMaximizeMenu(decoration, menu);
+        createMaximizeMenu(decoration);
+        verify(menu).show(anyBoolean(), anyInt(), anyBoolean(), anyBoolean(), any(), any(), any(),
+                any(), mOnMaxMenuHoverChangeListener.capture(), any());
 
         mOnMaxMenuHoverChangeListener.getValue().invoke(false);
 
@@ -940,7 +940,7 @@
         final DesktopModeWindowDecoration decoration = createWindowDecoration(taskInfo,
                 new FakeMaximizeMenuFactory(menu));
         decoration.setAppHeaderMaximizeButtonHovered(true);
-        createMaximizeMenu(decoration, menu);
+        createMaximizeMenu(decoration);
 
         decoration.setAppHeaderMaximizeButtonHovered(false);
 
@@ -958,7 +958,9 @@
         final MaximizeMenu menu = mock(MaximizeMenu.class);
         final DesktopModeWindowDecoration decoration = createWindowDecoration(taskInfo,
                 new FakeMaximizeMenuFactory(menu));
-        createMaximizeMenu(decoration, menu);
+        createMaximizeMenu(decoration);
+        verify(menu).show(anyBoolean(), anyInt(), anyBoolean(), anyBoolean(), any(), any(), any(),
+                any(), mOnMaxMenuHoverChangeListener.capture(), any());
 
         mOnMaxMenuHoverChangeListener.getValue().invoke(true);
 
@@ -971,13 +973,114 @@
         final MaximizeMenu menu = mock(MaximizeMenu.class);
         final DesktopModeWindowDecoration decoration = createWindowDecoration(taskInfo,
                 new FakeMaximizeMenuFactory(menu));
-        createMaximizeMenu(decoration, menu);
+        createMaximizeMenu(decoration);
+        verify(menu).show(anyBoolean(), anyInt(), anyBoolean(), anyBoolean(), any(), any(), any(),
+                any(), mOnMaxMenuHoverChangeListener.capture(), any());
 
         decoration.setAppHeaderMaximizeButtonHovered(true);
 
         verify(mMockHandler).removeCallbacks(any());
     }
 
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP)
+    public void createMaximizeMenu_taskRequestsImmersive_showsImmersiveOption() {
+        final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(true /* visible */);
+        taskInfo.requestedVisibleTypes = ~statusBars();
+        final MaximizeMenu menu = mock(MaximizeMenu.class);
+        final DesktopModeWindowDecoration decoration = createWindowDecoration(taskInfo,
+                new FakeMaximizeMenuFactory(menu));
+
+        createMaximizeMenu(decoration);
+
+        verify(menu).show(
+                anyBoolean(),
+                anyInt(),
+                /* showImmersiveOption= */ eq(true),
+                anyBoolean(),
+                any(),
+                any(),
+                any(),
+                any(),
+                any(),
+                any()
+        );
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP)
+    public void createMaximizeMenu_taskDoesNotRequestImmersive_hiddenImmersiveOption() {
+        final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(true /* visible */);
+        taskInfo.requestedVisibleTypes = statusBars();
+        final MaximizeMenu menu = mock(MaximizeMenu.class);
+        final DesktopModeWindowDecoration decoration = createWindowDecoration(taskInfo,
+                new FakeMaximizeMenuFactory(menu));
+
+        createMaximizeMenu(decoration);
+
+        verify(menu).show(
+                anyBoolean(),
+                anyInt(),
+                /* showImmersiveOption= */ eq(false),
+                anyBoolean(),
+                any(),
+                any(),
+                any(),
+                any(),
+                any(),
+                any()
+        );
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP)
+    public void createMaximizeMenu_taskResizable_showsSnapOptions() {
+        final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(true /* visible */);
+        taskInfo.isResizeable = true;
+        final MaximizeMenu menu = mock(MaximizeMenu.class);
+        final DesktopModeWindowDecoration decoration = createWindowDecoration(taskInfo,
+                new FakeMaximizeMenuFactory(menu));
+
+        createMaximizeMenu(decoration);
+
+        verify(menu).show(
+                anyBoolean(),
+                anyInt(),
+                anyBoolean(),
+                /* showSnapOptions= */ eq(true),
+                any(),
+                any(),
+                any(),
+                any(),
+                any(),
+                any()
+        );
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP)
+    public void createMaximizeMenu_taskUnresizable_hiddenSnapOptions() {
+        final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(true /* visible */);
+        taskInfo.isResizeable = false;
+        final MaximizeMenu menu = mock(MaximizeMenu.class);
+        final DesktopModeWindowDecoration decoration = createWindowDecoration(taskInfo,
+                new FakeMaximizeMenuFactory(menu));
+
+        createMaximizeMenu(decoration);
+
+        verify(menu).show(
+                anyBoolean(),
+                anyInt(),
+                anyBoolean(),
+                /* showSnapOptions= */ eq(false),
+                any(),
+                any(),
+                any(),
+                any(),
+                any(),
+                any()
+        );
+    }
 
     @Test
     @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_TO_WEB)
@@ -1335,13 +1438,13 @@
                 anyInt(), anyInt(), anyInt(), anyInt());
     }
 
-    private void createMaximizeMenu(DesktopModeWindowDecoration decoration, MaximizeMenu menu) {
+    private void createMaximizeMenu(DesktopModeWindowDecoration decoration) {
         final Function0<Unit> l = () -> Unit.INSTANCE;
         decoration.setOnMaximizeOrRestoreClickListener(l);
+        decoration.setOnImmersiveOrRestoreClickListener(l);
         decoration.setOnLeftSnapClickListener(l);
         decoration.setOnRightSnapClickListener(l);
         decoration.createMaximizeMenu();
-        verify(menu).show(any(), any(), any(), mOnMaxMenuHoverChangeListener.capture(), any());
     }
 
     private void fillRoundedCornersResources(int fillValue) {
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragResizeWindowGeometryTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragResizeWindowGeometryTests.java
index 57469bf..e7d328e 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragResizeWindowGeometryTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragResizeWindowGeometryTests.java
@@ -65,7 +65,7 @@
     private static final int LARGE_CORNER_SIZE = FINE_CORNER_SIZE + 10;
     private static final DragResizeWindowGeometry GEOMETRY = new DragResizeWindowGeometry(
             TASK_CORNER_RADIUS, TASK_SIZE, EDGE_RESIZE_THICKNESS, EDGE_RESIZE_HANDLE_INSET,
-            FINE_CORNER_SIZE, LARGE_CORNER_SIZE);
+            FINE_CORNER_SIZE, LARGE_CORNER_SIZE, DragResizeWindowGeometry.DisabledEdge.NONE);
     // Points in the edge resize handle. Note that coordinates start from the top left.
     private static final Point TOP_EDGE_POINT = new Point(TASK_SIZE.getWidth() / 2,
             -EDGE_RESIZE_THICKNESS / 2);
@@ -100,23 +100,25 @@
                         GEOMETRY,
                         new DragResizeWindowGeometry(TASK_CORNER_RADIUS, TASK_SIZE,
                                 EDGE_RESIZE_THICKNESS, EDGE_RESIZE_HANDLE_INSET, FINE_CORNER_SIZE,
-                                LARGE_CORNER_SIZE))
+                                LARGE_CORNER_SIZE, DragResizeWindowGeometry.DisabledEdge.NONE))
                 .addEqualityGroup(
                         new DragResizeWindowGeometry(TASK_CORNER_RADIUS, TASK_SIZE,
                                 EDGE_RESIZE_THICKNESS + 10, EDGE_RESIZE_HANDLE_INSET,
-                                FINE_CORNER_SIZE, LARGE_CORNER_SIZE),
+                                FINE_CORNER_SIZE, LARGE_CORNER_SIZE,
+                                DragResizeWindowGeometry.DisabledEdge.NONE),
                         new DragResizeWindowGeometry(TASK_CORNER_RADIUS, TASK_SIZE,
                                 EDGE_RESIZE_THICKNESS + 10, EDGE_RESIZE_HANDLE_INSET,
-                                FINE_CORNER_SIZE, LARGE_CORNER_SIZE))
+                                FINE_CORNER_SIZE, LARGE_CORNER_SIZE,
+                                DragResizeWindowGeometry.DisabledEdge.NONE))
                 .addEqualityGroup(
                         new DragResizeWindowGeometry(TASK_CORNER_RADIUS, TASK_SIZE,
                                 EDGE_RESIZE_THICKNESS + 10, EDGE_RESIZE_HANDLE_INSET,
                                 FINE_CORNER_SIZE,
-                                LARGE_CORNER_SIZE + 5),
+                                LARGE_CORNER_SIZE + 5, DragResizeWindowGeometry.DisabledEdge.NONE),
                         new DragResizeWindowGeometry(TASK_CORNER_RADIUS, TASK_SIZE,
                                 EDGE_RESIZE_THICKNESS + 10, EDGE_RESIZE_HANDLE_INSET,
                                 FINE_CORNER_SIZE,
-                                LARGE_CORNER_SIZE + 5))
+                                LARGE_CORNER_SIZE + 5, DragResizeWindowGeometry.DisabledEdge.NONE))
                 .testEquals();
     }
 
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositionerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositionerTest.kt
index ca1f9ab..3b80cb4 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositionerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositionerTest.kt
@@ -47,6 +47,7 @@
 import java.util.function.Supplier
 import org.mockito.Mockito.eq
 import org.mockito.Mockito.mock
+import org.mockito.kotlin.times
 import org.mockito.Mockito.`when` as whenever
 
 /**
@@ -66,7 +67,7 @@
     @Mock
     private lateinit var mockWindowDecoration: WindowDecoration<*>
     @Mock
-    private lateinit var mockDragStartListener: DragPositioningCallbackUtility.DragStartListener
+    private lateinit var mockDragEventListener: DragPositioningCallbackUtility.DragEventListener
 
     @Mock
     private lateinit var taskToken: WindowContainerToken
@@ -140,7 +141,7 @@
                 mockTransitions,
                 mockWindowDecoration,
                 mockDisplayController,
-                mockDragStartListener,
+                mockDragEventListener,
                 mockTransactionFactory
         )
     }
@@ -220,6 +221,7 @@
                         change.configuration.windowConfiguration.bounds == rectAfterMove
             }
         })
+        verify(mockDragEventListener, times(1)).onDragMove(eq(TASK_ID))
 
         taskPositioner.onDragPositioningEnd(
                 STARTING_BOUNDS.left.toFloat() + 10,
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositionerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositionerTest.kt
index 1dfbd67..e7df864 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositionerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositionerTest.kt
@@ -79,7 +79,7 @@
     @Mock
     private lateinit var mockDesktopWindowDecoration: DesktopModeWindowDecoration
     @Mock
-    private lateinit var mockDragStartListener: DragPositioningCallbackUtility.DragStartListener
+    private lateinit var mockDragEventListener: DragPositioningCallbackUtility.DragEventListener
 
     @Mock
     private lateinit var taskToken: WindowContainerToken
@@ -156,7 +156,7 @@
                         mockShellTaskOrganizer,
                         mockDesktopWindowDecoration,
                         mockDisplayController,
-                        mockDragStartListener,
+                        mockDragEventListener,
                         mockTransactionFactory,
                         mockTransitions,
                         mockInteractionJankMonitor,
@@ -433,6 +433,7 @@
 
         // isResizingOrAnimating should be set to true after move during a resize
         Assert.assertTrue(taskPositioner.isResizingOrAnimating)
+        verify(mockDragEventListener, times(1)).onDragMove(eq(TASK_ID))
 
         taskPositioner.onDragPositioningEnd(
                 STARTING_BOUNDS.left.toFloat(),
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDecorViewModelTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDecorViewModelTest.kt
new file mode 100644
index 0000000..0ccd424
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDecorViewModelTest.kt
@@ -0,0 +1,164 @@
+/*
+ * 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.windowdecor.tiling
+
+import android.content.Context
+import android.graphics.Rect
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.wm.shell.RootTaskDisplayAreaOrganizer
+import com.android.wm.shell.ShellTaskOrganizer
+import com.android.wm.shell.ShellTestCase
+import com.android.wm.shell.common.DisplayController
+import com.android.wm.shell.common.SyncTransactionQueue
+import com.android.wm.shell.desktopmode.DesktopRepository
+import com.android.wm.shell.desktopmode.DesktopTasksController
+import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createFreeformTask
+import com.android.wm.shell.desktopmode.ReturnToDragStartAnimator
+import com.android.wm.shell.desktopmode.ToggleResizeDesktopTaskTransitionHandler
+import com.android.wm.shell.transition.Transitions
+import com.android.wm.shell.windowdecor.DesktopModeWindowDecoration
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.any
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.times
+import org.mockito.kotlin.verify
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class DesktopTilingDecorViewModelTest : ShellTestCase() {
+    private val contextMock: Context = mock()
+    private val displayControllerMock: DisplayController = mock()
+    private val rootTdaOrganizerMock: RootTaskDisplayAreaOrganizer = mock()
+    private val syncQueueMock: SyncTransactionQueue = mock()
+    private val transitionsMock: Transitions = mock()
+    private val shellTaskOrganizerMock: ShellTaskOrganizer = mock()
+    private val desktopRepository: DesktopRepository = mock()
+    private val toggleResizeDesktopTaskTransitionHandlerMock:
+        ToggleResizeDesktopTaskTransitionHandler =
+        mock()
+    private val returnToDragStartAnimatorMock: ReturnToDragStartAnimator = mock()
+
+    private val desktopModeWindowDecorationMock: DesktopModeWindowDecoration = mock()
+    private val desktopTilingDecoration: DesktopTilingWindowDecoration = mock()
+    private lateinit var desktopTilingDecorViewModel: DesktopTilingDecorViewModel
+
+    @Before
+    fun setUp() {
+        desktopTilingDecorViewModel =
+            DesktopTilingDecorViewModel(
+                contextMock,
+                displayControllerMock,
+                rootTdaOrganizerMock,
+                syncQueueMock,
+                transitionsMock,
+                shellTaskOrganizerMock,
+                toggleResizeDesktopTaskTransitionHandlerMock,
+                returnToDragStartAnimatorMock,
+                desktopRepository,
+            )
+    }
+
+    @Test
+    fun testTiling_shouldCreate_newTilingDecoration() {
+        val task1 = createFreeformTask()
+        val task2 = createFreeformTask()
+        task1.displayId = 1
+        task2.displayId = 2
+
+        desktopTilingDecorViewModel.snapToHalfScreen(
+            task1,
+            desktopModeWindowDecorationMock,
+            DesktopTasksController.SnapPosition.LEFT,
+            BOUNDS,
+        )
+        assertThat(desktopTilingDecorViewModel.tilingTransitionHandlerByDisplayId.size())
+            .isEqualTo(1)
+        desktopTilingDecorViewModel.snapToHalfScreen(
+            task2,
+            desktopModeWindowDecorationMock,
+            DesktopTasksController.SnapPosition.LEFT,
+            BOUNDS,
+        )
+        assertThat(desktopTilingDecorViewModel.tilingTransitionHandlerByDisplayId.size())
+            .isEqualTo(2)
+    }
+
+    @Test
+    fun removeTile_shouldCreate_newTilingDecoration() {
+        val task1 = createFreeformTask()
+        task1.displayId = 1
+        desktopTilingDecorViewModel.tilingTransitionHandlerByDisplayId.put(
+            1,
+            desktopTilingDecoration,
+        )
+        desktopTilingDecorViewModel.removeTaskIfTiled(task1.displayId, task1.taskId)
+
+        verify(desktopTilingDecoration, times(1)).removeTaskIfTiled(any(), any(), any())
+    }
+
+    @Test
+    fun moveTaskToFront_shouldRoute_toCorrectTilingDecoration() {
+
+        val task1 = createFreeformTask()
+        task1.displayId = 1
+        desktopTilingDecorViewModel.tilingTransitionHandlerByDisplayId.put(
+            1,
+            desktopTilingDecoration,
+        )
+        desktopTilingDecorViewModel.moveTaskToFrontIfTiled(task1)
+
+        verify(desktopTilingDecoration, times(1)).moveTiledPairToFront(any())
+    }
+
+    @Test
+    fun overviewAnimation_starting_ShouldNotifyAllDecorations() {
+        desktopTilingDecorViewModel.tilingTransitionHandlerByDisplayId.put(
+            1,
+            desktopTilingDecoration,
+        )
+        desktopTilingDecorViewModel.tilingTransitionHandlerByDisplayId.put(
+            2,
+            desktopTilingDecoration,
+        )
+        desktopTilingDecorViewModel.onOverviewAnimationStateChange(true)
+
+        verify(desktopTilingDecoration, times(2)).onOverviewAnimationStateChange(any())
+    }
+
+    @Test
+    fun userChange_starting_allTilingSessionsShouldBeDestroyed() {
+        desktopTilingDecorViewModel.tilingTransitionHandlerByDisplayId.put(
+            1,
+            desktopTilingDecoration,
+        )
+        desktopTilingDecorViewModel.tilingTransitionHandlerByDisplayId.put(
+            2,
+            desktopTilingDecoration,
+        )
+
+        desktopTilingDecorViewModel.onUserChange()
+
+        verify(desktopTilingDecoration, times(2)).onUserChange()
+    }
+
+    companion object {
+        private val BOUNDS = Rect(1, 2, 3, 4)
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDividerWindowManagerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDividerWindowManagerTest.kt
new file mode 100644
index 0000000..0ee3f46
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDividerWindowManagerTest.kt
@@ -0,0 +1,99 @@
+/*
+ * 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.windowdecor.tiling
+
+import android.content.res.Configuration
+import android.graphics.Rect
+import android.testing.AndroidTestingRunner
+import android.view.SurfaceControl
+import androidx.test.annotation.UiThreadTest
+import androidx.test.filters.SmallTest
+import com.android.wm.shell.ShellTestCase
+import com.android.wm.shell.common.SyncTransactionQueue
+import java.util.function.Supplier
+import kotlin.test.Test
+import org.junit.Before
+import org.junit.runner.RunWith
+import org.mockito.kotlin.any
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.times
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class DesktopTilingDividerWindowManagerTest : ShellTestCase() {
+    private lateinit var config: Configuration
+
+    private var windowName: String = "Tiling"
+
+    private val leashMock = mock<SurfaceControl>()
+
+    private val syncQueueMock = mock<SyncTransactionQueue>()
+
+    private val transitionHandlerMock = mock<DesktopTilingWindowDecoration>()
+
+    private val transactionSupplierMock = mock<Supplier<SurfaceControl.Transaction>>()
+
+    private val surfaceControl = mock<SurfaceControl>()
+
+    private val transaction = mock<SurfaceControl.Transaction>()
+
+    private lateinit var desktopTilingWindowManager: DesktopTilingDividerWindowManager
+
+    @Before
+    fun setup() {
+        config = Configuration()
+        config.setToDefaults()
+        desktopTilingWindowManager =
+            DesktopTilingDividerWindowManager(
+                config,
+                windowName,
+                mContext,
+                leashMock,
+                syncQueueMock,
+                transitionHandlerMock,
+                transactionSupplierMock,
+                BOUNDS,
+            )
+    }
+
+    @Test
+    @UiThreadTest
+    fun testWindowManager_isInitialisedAndReleased() {
+        whenever(transactionSupplierMock.get()).thenReturn(transaction)
+        whenever(transaction.hide(any())).thenReturn(transaction)
+        whenever(transaction.setRelativeLayer(any(), any(), any())).thenReturn(transaction)
+        whenever(transaction.setPosition(any(), any(), any())).thenReturn(transaction)
+        whenever(transaction.remove(any())).thenReturn(transaction)
+
+        desktopTilingWindowManager.generateViewHost(surfaceControl)
+
+        // Ensure a surfaceControl transaction runs to show the divider.
+        verify(transactionSupplierMock, times(1)).get()
+        verify(syncQueueMock, times(1)).runInSync(any())
+
+        desktopTilingWindowManager.release()
+        verify(transaction, times(1)).hide(any())
+        verify(transaction, times(1)).remove(any())
+        verify(transaction, times(1)).apply()
+    }
+
+    companion object {
+        private val BOUNDS = Rect(1, 2, 3, 4)
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecorationTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecorationTest.kt
new file mode 100644
index 0000000..0b04a21
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecorationTest.kt
@@ -0,0 +1,518 @@
+/*
+ * 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.windowdecor.tiling
+
+import android.app.ActivityManager
+import android.content.Context
+import android.content.res.Resources
+import android.graphics.Rect
+import android.os.IBinder
+import android.testing.AndroidTestingRunner
+import android.view.SurfaceControl
+import android.view.WindowManager.TRANSIT_CHANGE
+import android.view.WindowManager.TRANSIT_TO_FRONT
+import android.window.TransitionInfo
+import android.window.WindowContainerTransaction
+import androidx.test.filters.SmallTest
+import com.android.wm.shell.RootTaskDisplayAreaOrganizer
+import com.android.wm.shell.ShellTaskOrganizer
+import com.android.wm.shell.ShellTestCase
+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.DesktopRepository
+import com.android.wm.shell.desktopmode.DesktopTasksController
+import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createFreeformTask
+import com.android.wm.shell.desktopmode.ReturnToDragStartAnimator
+import com.android.wm.shell.desktopmode.ToggleResizeDesktopTaskTransitionHandler
+import com.android.wm.shell.transition.Transitions
+import com.android.wm.shell.windowdecor.DesktopModeWindowDecoration
+import com.android.wm.shell.windowdecor.DragResizeWindowGeometry
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.ArgumentMatchers.eq
+import org.mockito.Captor
+import org.mockito.kotlin.any
+import org.mockito.kotlin.capture
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.never
+import org.mockito.kotlin.times
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class DesktopTilingWindowDecorationTest : ShellTestCase() {
+
+    private val context: Context = mock()
+
+    private val syncQueue: SyncTransactionQueue = mock()
+
+    private val displayController: DisplayController = mock()
+    private val displayId: Int = 0
+
+    private val rootTdaOrganizer: RootTaskDisplayAreaOrganizer = mock()
+
+    private val transitions: Transitions = mock()
+
+    private val shellTaskOrganizer: ShellTaskOrganizer = mock()
+
+    private val toggleResizeDesktopTaskTransitionHandler: ToggleResizeDesktopTaskTransitionHandler =
+        mock()
+
+    private val returnToDragStartAnimator: ReturnToDragStartAnimator = mock()
+
+    private val desktopWindowDecoration: DesktopModeWindowDecoration = mock()
+
+    private val displayLayout: DisplayLayout = mock()
+
+    private val resources: Resources = mock()
+    private val surfaceControlMock: SurfaceControl = mock()
+    private val transaction: SurfaceControl.Transaction = mock()
+    private val tiledTaskHelper: DesktopTilingWindowDecoration.AppResizingHelper = mock()
+    private val transition: IBinder = mock()
+    private val info: TransitionInfo = mock()
+    private val finishCallback: Transitions.TransitionFinishCallback = mock()
+    private val desktopRepository: DesktopRepository = mock()
+    private val desktopTilingDividerWindowManager: DesktopTilingDividerWindowManager = mock()
+    private lateinit var tilingDecoration: DesktopTilingWindowDecoration
+
+    private val split_divider_width = 10
+
+    @Captor private lateinit var wctCaptor: ArgumentCaptor<WindowContainerTransaction>
+
+    @Before
+    fun setUp() {
+        tilingDecoration =
+            DesktopTilingWindowDecoration(
+                context,
+                syncQueue,
+                displayController,
+                displayId,
+                rootTdaOrganizer,
+                transitions,
+                shellTaskOrganizer,
+                toggleResizeDesktopTaskTransitionHandler,
+                returnToDragStartAnimator,
+                desktopRepository,
+            )
+    }
+
+    @Test
+    fun taskTiled_toCorrectBounds_leftTile() {
+        val task1 = createFreeformTask()
+        val stableBounds = STABLE_BOUNDS_MOCK
+        whenever(displayController.getDisplayLayout(any())).thenReturn(displayLayout)
+        whenever(displayLayout.getStableBounds(any())).thenAnswer { i ->
+            (i.arguments.first() as Rect).set(stableBounds)
+        }
+        whenever(context.resources).thenReturn(resources)
+        whenever(resources.getDimensionPixelSize(any())).thenReturn(split_divider_width)
+
+        tilingDecoration.onAppTiled(
+            task1,
+            desktopWindowDecoration,
+            DesktopTasksController.SnapPosition.LEFT,
+            BOUNDS,
+        )
+
+        verify(toggleResizeDesktopTaskTransitionHandler).startTransition(capture(wctCaptor), any())
+        for (change in wctCaptor.value.changes) {
+            val bounds = change.value.configuration.windowConfiguration.bounds
+            val leftBounds = getLeftTaskBounds()
+            assertRectEqual(bounds, leftBounds)
+        }
+    }
+
+    @Test
+    fun taskTiled_toCorrectBounds_rightTile() {
+        // Setup
+        val task1 = createFreeformTask()
+        val stableBounds = STABLE_BOUNDS_MOCK
+        whenever(displayController.getDisplayLayout(any())).thenReturn(displayLayout)
+        whenever(displayLayout.getStableBounds(any())).thenAnswer { i ->
+            (i.arguments.first() as Rect).set(stableBounds)
+        }
+        whenever(context.resources).thenReturn(resources)
+        whenever(resources.getDimensionPixelSize(any())).thenReturn(split_divider_width)
+
+        tilingDecoration.onAppTiled(
+            task1,
+            desktopWindowDecoration,
+            DesktopTasksController.SnapPosition.RIGHT,
+            BOUNDS,
+        )
+
+        verify(toggleResizeDesktopTaskTransitionHandler).startTransition(capture(wctCaptor), any())
+        for (change in wctCaptor.value.changes) {
+            val bounds = change.value.configuration.windowConfiguration.bounds
+            val leftBounds = getRightTaskBounds()
+            assertRectEqual(bounds, leftBounds)
+        }
+    }
+
+    @Test
+    fun taskTiled_notAnimated_whenTilingPositionNotChange() {
+        val task1 = createFreeformTask()
+        val stableBounds = STABLE_BOUNDS_MOCK
+        whenever(displayController.getDisplayLayout(any())).thenReturn(displayLayout)
+        whenever(displayLayout.getStableBounds(any())).thenAnswer { i ->
+            (i.arguments.first() as Rect).set(stableBounds)
+        }
+        whenever(context.resources).thenReturn(resources)
+        whenever(resources.getDimensionPixelSize(any())).thenReturn(split_divider_width)
+        whenever(desktopWindowDecoration.getLeash()).thenReturn(surfaceControlMock)
+
+        tilingDecoration.onAppTiled(
+            task1,
+            desktopWindowDecoration,
+            DesktopTasksController.SnapPosition.LEFT,
+            BOUNDS,
+        )
+        task1.configuration.windowConfiguration.setBounds(getLeftTaskBounds())
+        tilingDecoration.onAppTiled(
+            task1,
+            desktopWindowDecoration,
+            DesktopTasksController.SnapPosition.LEFT,
+            NON_STABLE_BOUNDS_MOCK,
+        )
+
+        verify(toggleResizeDesktopTaskTransitionHandler, times(1))
+            .startTransition(capture(wctCaptor), any())
+        verify(returnToDragStartAnimator, times(1)).start(any(), any(), any(), any(), any())
+        for (change in wctCaptor.value.changes) {
+            val bounds = change.value.configuration.windowConfiguration.bounds
+            val leftBounds = getLeftTaskBounds()
+            assertRectEqual(bounds, leftBounds)
+        }
+    }
+
+    @Test
+    fun taskNotTiled_notBroughtToFront_tilingNotInitialised() {
+        val task1 = createFreeformTask()
+        val task2 = createFreeformTask()
+        val stableBounds = STABLE_BOUNDS_MOCK
+        whenever(displayController.getDisplayLayout(any())).thenReturn(displayLayout)
+        whenever(displayLayout.getStableBounds(any())).thenAnswer { i ->
+            (i.arguments.first() as Rect).set(stableBounds)
+        }
+        whenever(context.resources).thenReturn(resources)
+        whenever(resources.getDimensionPixelSize(any())).thenReturn(split_divider_width)
+
+        tilingDecoration.onAppTiled(
+            task1,
+            desktopWindowDecoration,
+            DesktopTasksController.SnapPosition.RIGHT,
+            BOUNDS,
+        )
+
+        assertThat(tilingDecoration.moveTiledPairToFront(task2)).isFalse()
+        verify(transitions, never()).startTransition(any(), any(), any())
+    }
+
+    @Test
+    fun taskNotTiled_notBroughtToFront_taskNotTiled() {
+        val task1 = createFreeformTask()
+        val task2 = createFreeformTask()
+        val task3 = createFreeformTask()
+        val stableBounds = STABLE_BOUNDS_MOCK
+        whenever(displayController.getDisplayLayout(any())).thenReturn(displayLayout)
+        whenever(displayLayout.getStableBounds(any())).thenAnswer { i ->
+            (i.arguments.first() as Rect).set(stableBounds)
+        }
+        whenever(context.resources).thenReturn(resources)
+        whenever(resources.getDimensionPixelSize(any())).thenReturn(split_divider_width)
+
+        tilingDecoration.onAppTiled(
+            task1,
+            desktopWindowDecoration,
+            DesktopTasksController.SnapPosition.RIGHT,
+            BOUNDS,
+        )
+        tilingDecoration.onAppTiled(
+            task2,
+            desktopWindowDecoration,
+            DesktopTasksController.SnapPosition.LEFT,
+            BOUNDS,
+        )
+
+        assertThat(tilingDecoration.moveTiledPairToFront(task3)).isFalse()
+        verify(transitions, never()).startTransition(any(), any(), any())
+    }
+
+    @Test
+    fun taskTiled_broughtToFront_alreadyInFrontNoAction() {
+        val task1 = createFreeformTask()
+        val task2 = createFreeformTask()
+        val stableBounds = STABLE_BOUNDS_MOCK
+        whenever(displayController.getDisplayLayout(any())).thenReturn(displayLayout)
+        whenever(displayLayout.getStableBounds(any())).thenAnswer { i ->
+            (i.arguments.first() as Rect).set(stableBounds)
+        }
+        whenever(context.resources).thenReturn(resources)
+        whenever(resources.getDimensionPixelSize(any())).thenReturn(split_divider_width)
+
+        tilingDecoration.onAppTiled(
+            task1,
+            desktopWindowDecoration,
+            DesktopTasksController.SnapPosition.RIGHT,
+            BOUNDS,
+        )
+        tilingDecoration.onAppTiled(
+            task2,
+            desktopWindowDecoration,
+            DesktopTasksController.SnapPosition.LEFT,
+            BOUNDS,
+        )
+        task1.isFocused = true
+
+        assertThat(tilingDecoration.moveTiledPairToFront(task1)).isFalse()
+        verify(transitions, never()).startTransition(any(), any(), any())
+    }
+
+    @Test
+    fun taskTiled_broughtToFront_bringToFront() {
+        val task1 = createFreeformTask()
+        val task2 = createFreeformTask()
+        val task3 = createFreeformTask()
+        val stableBounds = STABLE_BOUNDS_MOCK
+        whenever(displayLayout.getStableBounds(any())).thenAnswer { i ->
+            (i.arguments.first() as Rect).set(stableBounds)
+        }
+        whenever(context.resources).thenReturn(resources)
+        whenever(resources.getDimensionPixelSize(any())).thenReturn(split_divider_width)
+        whenever(desktopWindowDecoration.getLeash()).thenReturn(surfaceControlMock)
+        whenever(desktopRepository.isVisibleTask(any())).thenReturn(true)
+        tilingDecoration.onAppTiled(
+            task1,
+            desktopWindowDecoration,
+            DesktopTasksController.SnapPosition.RIGHT,
+            BOUNDS,
+        )
+        tilingDecoration.onAppTiled(
+            task2,
+            desktopWindowDecoration,
+            DesktopTasksController.SnapPosition.LEFT,
+            BOUNDS,
+        )
+        task1.isFocused = true
+        task3.isFocused = true
+
+        assertThat(tilingDecoration.moveTiledPairToFront(task3)).isFalse()
+        assertThat(tilingDecoration.moveTiledPairToFront(task1)).isTrue()
+        verify(transitions, times(1)).startTransition(eq(TRANSIT_TO_FRONT), any(), eq(null))
+    }
+
+    @Test
+    fun taskTiledTasks_NotResized_BeforeTouchEndArrival() {
+        // Setup
+        val task1 = createFreeformTask()
+        val task2 = createFreeformTask()
+        val stableBounds = STABLE_BOUNDS_MOCK
+        whenever(displayController.getDisplayLayout(any())).thenReturn(displayLayout)
+        whenever(displayLayout.getStableBounds(any())).thenAnswer { i ->
+            (i.arguments.first() as Rect).set(stableBounds)
+        }
+        whenever(context.resources).thenReturn(resources)
+        whenever(resources.getDimensionPixelSize(any())).thenReturn(split_divider_width)
+        desktopWindowDecoration.mTaskInfo = task1
+        task1.minWidth = 0
+        task1.minHeight = 0
+        initTiledTaskHelperMock(task1)
+        desktopWindowDecoration.mDecorWindowContext = context
+        whenever(resources.getBoolean(any())).thenReturn(true)
+
+        // Act
+        tilingDecoration.onAppTiled(
+            task1,
+            desktopWindowDecoration,
+            DesktopTasksController.SnapPosition.RIGHT,
+            BOUNDS,
+        )
+        tilingDecoration.onAppTiled(
+            task2,
+            desktopWindowDecoration,
+            DesktopTasksController.SnapPosition.LEFT,
+            BOUNDS,
+        )
+
+        tilingDecoration.leftTaskResizingHelper = tiledTaskHelper
+        tilingDecoration.rightTaskResizingHelper = tiledTaskHelper
+        tilingDecoration.onDividerHandleMoved(BOUNDS, transaction)
+
+        // Assert
+        verify(transaction, times(1)).apply()
+        // Show should be called twice for each tiled app, to show the veil and the icon for each
+        // of them.
+        verify(tiledTaskHelper, times(2)).showVeil(any())
+
+        // Move again
+        tilingDecoration.onDividerHandleMoved(BOUNDS, transaction)
+        verify(tiledTaskHelper, times(2)).updateVeil(any())
+        verify(transitions, never()).startTransition(any(), any(), any())
+
+        // End moving, no startTransition because bounds did not change.
+        tiledTaskHelper.newBounds.set(BOUNDS)
+        tilingDecoration.onDividerHandleDragEnd(BOUNDS, transaction)
+        verify(tiledTaskHelper, times(2)).hideVeil()
+        verify(transitions, never()).startTransition(any(), any(), any())
+
+        // Move then end again with bounds changing to ensure startTransition is called.
+        tilingDecoration.onDividerHandleMoved(BOUNDS, transaction)
+        tilingDecoration.onDividerHandleDragEnd(BOUNDS, transaction)
+        verify(transitions, times(1))
+            .startTransition(eq(TRANSIT_CHANGE), any(), eq(tilingDecoration))
+        // No hide veil until start animation is called.
+        verify(tiledTaskHelper, times(2)).hideVeil()
+
+        tilingDecoration.startAnimation(transition, info, transaction, transaction, finishCallback)
+        // the startAnimation function should hide the veils.
+        verify(tiledTaskHelper, times(4)).hideVeil()
+    }
+
+    @Test
+    fun taskTiled_shouldBeRemoved_whenTileBroken() {
+        val task1 = createFreeformTask()
+        val stableBounds = STABLE_BOUNDS_MOCK
+        whenever(displayController.getDisplayLayout(any())).thenReturn(displayLayout)
+        whenever(displayLayout.getStableBounds(any())).thenAnswer { i ->
+            (i.arguments.first() as Rect).set(stableBounds)
+        }
+        whenever(context.resources).thenReturn(resources)
+        whenever(resources.getDimensionPixelSize(any())).thenReturn(split_divider_width)
+        whenever(tiledTaskHelper.taskInfo).thenReturn(task1)
+        whenever(tiledTaskHelper.desktopModeWindowDecoration).thenReturn(desktopWindowDecoration)
+        tilingDecoration.onAppTiled(
+            task1,
+            desktopWindowDecoration,
+            DesktopTasksController.SnapPosition.LEFT,
+            BOUNDS,
+        )
+        tilingDecoration.leftTaskResizingHelper = tiledTaskHelper
+
+        tilingDecoration.removeTaskIfTiled(task1.taskId)
+
+        assertThat(tilingDecoration.leftTaskResizingHelper).isNull()
+        verify(desktopWindowDecoration, times(1)).removeDragResizeListener(any())
+        verify(desktopWindowDecoration, times(1))
+            .updateDisabledResizingEdge(eq(DragResizeWindowGeometry.DisabledEdge.NONE), eq(false))
+        verify(tiledTaskHelper, times(1)).dispose()
+    }
+
+    @Test
+    fun taskNotTiled_shouldNotBeRemoved_whenNotTiled() {
+        val task1 = createFreeformTask()
+        val task2 = createFreeformTask()
+        val stableBounds = STABLE_BOUNDS_MOCK
+        whenever(displayController.getDisplayLayout(any())).thenReturn(displayLayout)
+        whenever(displayLayout.getStableBounds(any())).thenAnswer { i ->
+            (i.arguments.first() as Rect).set(stableBounds)
+        }
+        whenever(context.resources).thenReturn(resources)
+        whenever(resources.getDimensionPixelSize(any())).thenReturn(split_divider_width)
+        whenever(tiledTaskHelper.taskInfo).thenReturn(task1)
+        whenever(tiledTaskHelper.desktopModeWindowDecoration).thenReturn(desktopWindowDecoration)
+        tilingDecoration.onAppTiled(
+            task1,
+            desktopWindowDecoration,
+            DesktopTasksController.SnapPosition.LEFT,
+            BOUNDS,
+        )
+        tilingDecoration.leftTaskResizingHelper = tiledTaskHelper
+
+        tilingDecoration.removeTaskIfTiled(task2.taskId)
+
+        assertThat(tilingDecoration.leftTaskResizingHelper).isNotNull()
+        verify(desktopWindowDecoration, never()).removeDragResizeListener(any())
+        verify(desktopWindowDecoration, never()).updateDisabledResizingEdge(any(), any())
+        verify(tiledTaskHelper, never()).dispose()
+    }
+
+    @Test
+    fun tasksTiled_shouldBeRemoved_whenSessionDestroyed() {
+        val task1 = createFreeformTask()
+        val task2 = createFreeformTask()
+        val stableBounds = STABLE_BOUNDS_MOCK
+        whenever(displayController.getDisplayLayout(any())).thenReturn(displayLayout)
+        whenever(displayLayout.getStableBounds(any())).thenAnswer { i ->
+            (i.arguments.first() as Rect).set(stableBounds)
+        }
+        whenever(context.resources).thenReturn(resources)
+        whenever(resources.getDimensionPixelSize(any())).thenReturn(split_divider_width)
+        whenever(tiledTaskHelper.taskInfo).thenReturn(task1)
+        whenever(tiledTaskHelper.desktopModeWindowDecoration).thenReturn(desktopWindowDecoration)
+        tilingDecoration.onAppTiled(
+            task1,
+            desktopWindowDecoration,
+            DesktopTasksController.SnapPosition.LEFT,
+            BOUNDS,
+        )
+        tilingDecoration.onAppTiled(
+            task2,
+            desktopWindowDecoration,
+            DesktopTasksController.SnapPosition.RIGHT,
+            BOUNDS,
+        )
+        tilingDecoration.leftTaskResizingHelper = tiledTaskHelper
+        tilingDecoration.rightTaskResizingHelper = tiledTaskHelper
+        tilingDecoration.desktopTilingDividerWindowManager = desktopTilingDividerWindowManager
+
+        tilingDecoration.onUserChange()
+
+        assertThat(tilingDecoration.leftTaskResizingHelper).isNull()
+        assertThat(tilingDecoration.rightTaskResizingHelper).isNull()
+        verify(desktopWindowDecoration, times(2)).removeDragResizeListener(any())
+        verify(tiledTaskHelper, times(2)).dispose()
+    }
+
+    private fun initTiledTaskHelperMock(taskInfo: ActivityManager.RunningTaskInfo) {
+        whenever(tiledTaskHelper.bounds).thenReturn(BOUNDS)
+        whenever(tiledTaskHelper.taskInfo).thenReturn(taskInfo)
+        whenever(tiledTaskHelper.newBounds).thenReturn(Rect(BOUNDS))
+        whenever(tiledTaskHelper.desktopModeWindowDecoration).thenReturn(desktopWindowDecoration)
+    }
+
+    private fun assertRectEqual(rect1: Rect, rect2: Rect) {
+        assertThat(rect1.left).isEqualTo(rect2.left)
+        assertThat(rect1.right).isEqualTo(rect2.right)
+        assertThat(rect1.top).isEqualTo(rect2.top)
+        assertThat(rect1.bottom).isEqualTo(rect2.bottom)
+        return
+    }
+
+    private fun getRightTaskBounds(): Rect {
+        val stableBounds = STABLE_BOUNDS_MOCK
+        val destinationWidth = stableBounds.width() / 2
+        val leftBound = stableBounds.right - destinationWidth + split_divider_width / 2
+        return Rect(leftBound, stableBounds.top, stableBounds.right, stableBounds.bottom)
+    }
+
+    private fun getLeftTaskBounds(): Rect {
+        val stableBounds = STABLE_BOUNDS_MOCK
+        val destinationWidth = stableBounds.width() / 2
+        val rightBound = stableBounds.left + destinationWidth - split_divider_width / 2
+        return Rect(stableBounds.left, stableBounds.top, rightBound, stableBounds.bottom)
+    }
+
+    companion object {
+        private val NON_STABLE_BOUNDS_MOCK = Rect(50, 55, 100, 100)
+        private val STABLE_BOUNDS_MOCK = Rect(0, 0, 100, 100)
+        private val BOUNDS = Rect(1, 2, 3, 4)
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/TilingDividerViewTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/TilingDividerViewTest.kt
new file mode 100644
index 0000000..c96ce95
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/TilingDividerViewTest.kt
@@ -0,0 +1,135 @@
+/*
+ * 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.windowdecor.tiling
+
+import android.graphics.Rect
+import android.os.SystemClock
+import android.testing.AndroidTestingRunner
+import android.view.InputDevice
+import android.view.LayoutInflater
+import android.view.MotionEvent
+import android.view.View
+import androidx.test.annotation.UiThreadTest
+import androidx.test.filters.SmallTest
+import com.android.wm.shell.R
+import com.android.wm.shell.ShellTestCase
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.any
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.never
+import org.mockito.kotlin.times
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class TilingDividerViewTest : ShellTestCase() {
+
+    private lateinit var tilingDividerView: TilingDividerView
+
+    private val dividerMoveCallbackMock = mock<DividerMoveCallback>()
+
+    private val viewMock = mock<View>()
+
+    @Before
+    @UiThreadTest
+    fun setUp() {
+        tilingDividerView =
+            LayoutInflater.from(mContext).inflate(R.layout.tiling_split_divider, /* root= */ null)
+                as TilingDividerView
+        tilingDividerView.setup(dividerMoveCallbackMock, BOUNDS)
+        tilingDividerView.handleStartY = 0
+        tilingDividerView.handleEndY = 1500
+    }
+
+    @Test
+    @UiThreadTest
+    fun testCallbackOnTouch() {
+        val x = 5
+        val y = 5
+        val downTime: Long = SystemClock.uptimeMillis()
+
+        val downMotionEvent =
+            getMotionEvent(downTime, MotionEvent.ACTION_DOWN, x.toFloat(), y.toFloat())
+        tilingDividerView.handleMotionEvent(viewMock, downMotionEvent)
+        verify(dividerMoveCallbackMock, times(1)).onDividerMoveStart(any())
+
+        whenever(dividerMoveCallbackMock.onDividerMove(any())).thenReturn(true)
+        val motionEvent =
+            getMotionEvent(downTime, MotionEvent.ACTION_MOVE, x.toFloat(), y.toFloat())
+        tilingDividerView.handleMotionEvent(viewMock, motionEvent)
+        verify(dividerMoveCallbackMock, times(1)).onDividerMove(any())
+
+        val upMotionEvent =
+            getMotionEvent(downTime, MotionEvent.ACTION_UP, x.toFloat(), y.toFloat())
+        tilingDividerView.handleMotionEvent(viewMock, upMotionEvent)
+        verify(dividerMoveCallbackMock, times(1)).onDividerMovedEnd(any())
+    }
+
+    @Test
+    @UiThreadTest
+    fun testCallbackOnTouch_doesNotHappen_whenNoTouchMove() {
+        val x = 5
+        val y = 5
+        val downTime: Long = SystemClock.uptimeMillis()
+
+        val downMotionEvent =
+            getMotionEvent(downTime, MotionEvent.ACTION_DOWN, x.toFloat(), y.toFloat())
+        tilingDividerView.handleMotionEvent(viewMock, downMotionEvent)
+        verify(dividerMoveCallbackMock, times(1)).onDividerMoveStart(any())
+
+        val upMotionEvent =
+            getMotionEvent(downTime, MotionEvent.ACTION_UP, x.toFloat(), y.toFloat())
+        tilingDividerView.handleMotionEvent(viewMock, upMotionEvent)
+        verify(dividerMoveCallbackMock, never()).onDividerMovedEnd(any())
+    }
+
+    private fun getMotionEvent(eventTime: Long, action: Int, x: Float, y: Float): MotionEvent {
+        val properties = MotionEvent.PointerProperties()
+        properties.id = 0
+        properties.toolType = MotionEvent.TOOL_TYPE_UNKNOWN
+
+        val coords = MotionEvent.PointerCoords()
+        coords.pressure = 1f
+        coords.size = 1f
+        coords.x = x
+        coords.y = y
+
+        return MotionEvent.obtain(
+            eventTime,
+            eventTime,
+            action,
+            1,
+            arrayOf(properties),
+            arrayOf(coords),
+            0,
+            0,
+            1.0f,
+            1.0f,
+            0,
+            0,
+            InputDevice.SOURCE_TOUCHSCREEN,
+            0,
+        )
+    }
+
+    companion object {
+        private val BOUNDS = Rect(0, 0, 1500, 1500)
+    }
+}
diff --git a/libs/appfunctions/java/com/google/android/appfunctions/sidecar/AppFunctionManager.java b/libs/appfunctions/java/com/google/android/appfunctions/sidecar/AppFunctionManager.java
index 43377d8..6870446 100644
--- a/libs/appfunctions/java/com/google/android/appfunctions/sidecar/AppFunctionManager.java
+++ b/libs/appfunctions/java/com/google/android/appfunctions/sidecar/AppFunctionManager.java
@@ -16,7 +16,6 @@
 
 package com.google.android.appfunctions.sidecar;
 
-import android.Manifest;
 import android.annotation.CallbackExecutor;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
@@ -41,8 +40,6 @@
  * <p>This class wraps {@link android.app.appfunctions.AppFunctionManager} functionalities and
  * exposes it here as a sidecar library (avoiding direct dependency on the platform API).
  */
-// TODO(b/357551503): Implement get and set enabled app function APIs.
-// TODO(b/367329899): Add sidecar library to Android B builds.
 public final class AppFunctionManager {
     /**
      * The default state of the app function. Call {@link #setAppFunctionEnabled} with this to reset
@@ -70,9 +67,9 @@
     @IntDef(
             prefix = {"APP_FUNCTION_STATE_"},
             value = {
-                    APP_FUNCTION_STATE_DEFAULT,
-                    APP_FUNCTION_STATE_ENABLED,
-                    APP_FUNCTION_STATE_DISABLED
+                APP_FUNCTION_STATE_DEFAULT,
+                APP_FUNCTION_STATE_ENABLED,
+                APP_FUNCTION_STATE_DISABLED
             })
     @Retention(RetentionPolicy.SOURCE)
     public @interface EnabledState {}
@@ -102,6 +99,9 @@
      * <p>Proxies request and response to the underlying {@link
      * android.app.appfunctions.AppFunctionManager#executeAppFunction}, converting the request and
      * response in the appropriate type required by the function.
+     *
+     * <p>See {@link android.app.appfunctions.AppFunctionManager#executeAppFunction} for the
+     * documented behaviour of this method.
      */
     public void executeAppFunction(
             @NonNull ExecuteAppFunctionRequest sidecarRequest,
@@ -128,24 +128,8 @@
     /**
      * Returns a boolean through a callback, indicating whether the app function is enabled.
      *
-     * <p>* This method can only check app functions owned by the caller, or those where the caller
-     * has visibility to the owner package and holds either the {@link
-     * Manifest.permission#EXECUTE_APP_FUNCTIONS} or {@link
-     * Manifest.permission#EXECUTE_APP_FUNCTIONS_TRUSTED} permission.
-     *
-     * <p>If operation fails, the callback's {@link OutcomeReceiver#onError} is called with errors:
-     *
-     * <ul>
-     *   <li>{@link IllegalArgumentException}, if the function is not found or the caller does not
-     *       have access to it.
-     * </ul>
-     *
-     * @param functionIdentifier the identifier of the app function to check (unique within the
-     *     target package) and in most cases, these are automatically generated by the AppFunctions
-     *     SDK
-     * @param targetPackage the package name of the app function's owner
-     * @param executor the executor to run the request
-     * @param callback the callback to receive the function enabled check result
+     * <p>See {@link android.app.appfunctions.AppFunctionManager#isAppFunctionEnabled} for the
+     * documented behaviour of this method.
      */
     public void isAppFunctionEnabled(
             @NonNull String functionIdentifier,
@@ -158,20 +142,8 @@
     /**
      * Sets the enabled state of the app function owned by the calling package.
      *
-     * <p>If operation fails, the callback's {@link OutcomeReceiver#onError} is called with errors:
-     *
-     * <ul>
-     *   <li>{@link IllegalArgumentException}, if the function is not found or the caller does not
-     *       have access to it.
-     * </ul>
-     *
-     * @param functionIdentifier the identifier of the app function to enable (unique within the
-     *     calling package). In most cases, identifiers are automatically generated by the
-     *     AppFunctions SDK
-     * @param newEnabledState the new state of the app function
-     * @param executor the executor to run the callback
-     * @param callback the callback to receive the result of the function enablement. The call was
-     *     successful if no exception was thrown.
+     * <p>See {@link android.app.appfunctions.AppFunctionManager#setAppFunctionEnabled} for the
+     * documented behavoir of this method.
      */
     // Constants in @EnabledState should always mirror those in
     // android.app.appfunctions.AppFunctionManager.
diff --git a/libs/appfunctions/java/com/google/android/appfunctions/sidecar/ExecuteAppFunctionRequest.java b/libs/appfunctions/java/com/google/android/appfunctions/sidecar/ExecuteAppFunctionRequest.java
index fa6d2ff..593c521 100644
--- a/libs/appfunctions/java/com/google/android/appfunctions/sidecar/ExecuteAppFunctionRequest.java
+++ b/libs/appfunctions/java/com/google/android/appfunctions/sidecar/ExecuteAppFunctionRequest.java
@@ -34,8 +34,8 @@
     @NonNull private final String mTargetPackageName;
 
     /**
-     * Returns the unique string identifier of the app function to be executed. TODO(b/357551503):
-     * Document how callers can get the available function identifiers.
+     * The unique string identifier of the app function to be executed. This identifier is used to
+     * execute a specific app function.
      */
     @NonNull private final String mFunctionIdentifier;
 
@@ -49,8 +49,6 @@
      *
      * <p>The document may have missing parameters. Developers are advised to implement defensive
      * handling measures.
-     *
-     * <p>TODO(b/357551503): Document how function parameters can be obtained for function execution
      */
     @NonNull private final GenericDocument mParameters;
 
@@ -71,7 +69,19 @@
         return mTargetPackageName;
     }
 
-    /** Returns the unique string identifier of the app function to be executed. */
+    /**
+     * Returns the unique string identifier of the app function to be executed.
+     *
+     * <p>When there is a package change or the device starts up, the metadata of available
+     * functions is indexed by AppSearch. AppSearch stores the indexed information as {@code
+     * AppFunctionStaticMetadata} document.
+     *
+     * <p>The ID can be obtained by querying the {@code AppFunctionStaticMetadata} documents from
+     * AppSearch.
+     *
+     * <p>If the {@code functionId} provided is invalid, the caller will get an invalid argument
+     * response.
+     */
     @NonNull
     public String getFunctionIdentifier() {
         return mFunctionIdentifier;
@@ -83,6 +93,12 @@
      *
      * <p>The bundle may have missing parameters. Developers are advised to implement defensive
      * handling measures.
+     *
+     * <p>Similar to {@link #getFunctionIdentifier()} the parameters required by a function can be
+     * obtained by querying AppSearch for the corresponding {@code AppFunctionStaticMetadata}. This
+     * metadata will contain enough information for the caller to resolve the required parameters
+     * either using information from the metadata itself or using the AppFunction SDK for function
+     * callers.
      */
     @NonNull
     public GenericDocument getParameters() {
@@ -128,10 +144,7 @@
         @NonNull
         public ExecuteAppFunctionRequest build() {
             return new ExecuteAppFunctionRequest(
-                    mTargetPackageName,
-                    mFunctionIdentifier,
-                    mExtras,
-                    mParameters);
+                    mTargetPackageName, mFunctionIdentifier, mExtras, mParameters);
         }
     }
 }
diff --git a/libs/appfunctions/java/com/google/android/appfunctions/sidecar/ExecuteAppFunctionResponse.java b/libs/appfunctions/java/com/google/android/appfunctions/sidecar/ExecuteAppFunctionResponse.java
index 969e5d5..d5dfaeb 100644
--- a/libs/appfunctions/java/com/google/android/appfunctions/sidecar/ExecuteAppFunctionResponse.java
+++ b/libs/appfunctions/java/com/google/android/appfunctions/sidecar/ExecuteAppFunctionResponse.java
@@ -56,14 +56,15 @@
     /** The caller does not have the permission to execute an app function. */
     public static final int RESULT_DENIED = 1;
 
-    /** An unknown error occurred while processing the call in the AppFunctionService. */
+    /**
+     * An unknown error occurred while processing the call in the AppFunctionService.
+     *
+     * <p>This error is thrown when the service is connected in the remote application but an
+     * unexpected error is thrown from the bound application.
+     */
     public static final int RESULT_APP_UNKNOWN_ERROR = 2;
 
-    /**
-     * An internal error occurred within AppFunctionManagerService.
-     *
-     * <p>This error may be considered similar to {@link IllegalStateException}
-     */
+    /** An internal unexpected error coming from the system. */
     public static final int RESULT_INTERNAL_ERROR = 3;
 
     /**
diff --git a/libs/hwui/aconfig/hwui_flags.aconfig b/libs/hwui/aconfig/hwui_flags.aconfig
index 93df478..f255967 100644
--- a/libs/hwui/aconfig/hwui_flags.aconfig
+++ b/libs/hwui/aconfig/hwui_flags.aconfig
@@ -139,3 +139,10 @@
     purpose: PURPOSE_BUGFIX
   }
 }
+
+flag {
+  name: "bitmap_ashmem_long_name"
+  namespace: "core_graphics"
+  description: "Whether to have more information in ashmem filenames for bitmaps"
+  bug: "369619160"
+}
diff --git a/libs/hwui/hwui/Bitmap.cpp b/libs/hwui/hwui/Bitmap.cpp
index b73380e..f58dcc6 100644
--- a/libs/hwui/hwui/Bitmap.cpp
+++ b/libs/hwui/hwui/Bitmap.cpp
@@ -15,6 +15,7 @@
  */
 #include "Bitmap.h"
 
+#include <android-base/file.h>
 #include "HardwareBitmapUploader.h"
 #include "Properties.h"
 #ifdef __ANDROID__  // Layoutlib does not support render thread
@@ -54,8 +55,18 @@
 #include <SkPngEncoder.h>
 #include <SkWebpEncoder.h>
 
+#include <atomic>
 #include <limits>
 
+#ifdef __ANDROID__
+#include <com_android_graphics_hwui_flags.h>
+namespace hwui_flags = com::android::graphics::hwui::flags;
+#else
+namespace hwui_flags {
+constexpr bool bitmap_ashmem_long_name() { return false; }
+}
+#endif
+
 namespace android {
 
 #ifdef __ANDROID__
@@ -86,6 +97,28 @@
 }
 #endif
 
+// generate an ID for this Bitmap, id is a 64-bit integer of 3 parts:
+//   0000xxxxxx - the lower 6 decimal digits is a monotonically increasing number
+//   000x000000 - the 7th decimal digit is the storage type (see PixelStorageType)
+//   xxx0000000 - the 8th decimal digit and above is the current pid
+//
+//   e.g. 43231000076 - means this bitmap is the 76th bitmap created, has the
+//   storage type of 'Heap', and is created in a process with pid 4323.
+//
+//   NOTE:
+//   1) the monotonic number could increase beyond 1000,000 and wrap around, which
+//   only happens when more than 1,000,000 bitmaps have been created over time.
+//   This could result in two IDs being the same despite being really rare.
+//   2) the IDs are intentionally represented in decimal to make it easier to
+//   reason and associate with numbers shown in heap dump (mostly in decimal)
+//   and PIDs shown in different tools (mostly in decimal as well).
+uint64_t Bitmap::getId(PixelStorageType type) {
+    static std::atomic<uint64_t> idCounter{0};
+    return (idCounter.fetch_add(1) % 1000000)
+        + static_cast<uint64_t>(type) * 1000000
+        + static_cast<uint64_t>(getpid()) * 10000000;
+}
+
 bool Bitmap::computeAllocationSize(size_t rowBytes, int height, size_t* size) {
     return 0 <= height && height <= std::numeric_limits<size_t>::max() &&
            !__builtin_mul_overflow(rowBytes, (size_t)height, size) &&
@@ -117,6 +150,20 @@
     return wrapper;
 }
 
+std::string Bitmap::getAshmemId(const char* tag, uint64_t bitmapId,
+                                int width, int height, size_t size) {
+    if (!hwui_flags::bitmap_ashmem_long_name()) {
+        return "bitmap";
+    }
+    static std::string sCmdline = [] {
+        std::string temp;
+        android::base::ReadFileToString("/proc/self/cmdline", &temp);
+        return temp;
+    }();
+    return std::format("bitmap/{}-id_{}-{}x{}-size_{}-{}",
+                       tag, bitmapId, width, height, size, sCmdline);
+}
+
 sk_sp<Bitmap> Bitmap::allocateAshmemBitmap(SkBitmap* bitmap) {
     return allocateBitmap(bitmap, &Bitmap::allocateAshmemBitmap);
 }
@@ -124,7 +171,9 @@
 sk_sp<Bitmap> Bitmap::allocateAshmemBitmap(size_t size, const SkImageInfo& info, size_t rowBytes) {
 #ifdef __ANDROID__
     // Create new ashmem region with read/write priv
-    int fd = ashmem_create_region("bitmap", size);
+    uint64_t id = getId(PixelStorageType::Ashmem);
+    auto ashmemId = getAshmemId("allocate", id, info.width(), info.height(), size);
+    int fd = ashmem_create_region(ashmemId.c_str(), size);
     if (fd < 0) {
         return nullptr;
     }
@@ -140,7 +189,7 @@
         close(fd);
         return nullptr;
     }
-    return sk_sp<Bitmap>(new Bitmap(addr, fd, size, info, rowBytes));
+    return sk_sp<Bitmap>(new Bitmap(addr, fd, size, info, rowBytes, id));
 #else
     return Bitmap::allocateHeapBitmap(size, info, rowBytes);
 #endif
@@ -261,7 +310,8 @@
 Bitmap::Bitmap(void* address, size_t size, const SkImageInfo& info, size_t rowBytes)
         : SkPixelRef(info.width(), info.height(), address, rowBytes)
         , mInfo(validateAlpha(info))
-        , mPixelStorageType(PixelStorageType::Heap) {
+        , mPixelStorageType(PixelStorageType::Heap)
+        , mId(getId(mPixelStorageType)) {
     mPixelStorage.heap.address = address;
     mPixelStorage.heap.size = size;
     traceBitmapCreate();
@@ -270,16 +320,19 @@
 Bitmap::Bitmap(SkPixelRef& pixelRef, const SkImageInfo& info)
         : SkPixelRef(info.width(), info.height(), pixelRef.pixels(), pixelRef.rowBytes())
         , mInfo(validateAlpha(info))
-        , mPixelStorageType(PixelStorageType::WrappedPixelRef) {
+        , mPixelStorageType(PixelStorageType::WrappedPixelRef)
+        , mId(getId(mPixelStorageType)) {
     pixelRef.ref();
     mPixelStorage.wrapped.pixelRef = &pixelRef;
     traceBitmapCreate();
 }
 
-Bitmap::Bitmap(void* address, int fd, size_t mappedSize, const SkImageInfo& info, size_t rowBytes)
+Bitmap::Bitmap(void* address, int fd, size_t mappedSize, const SkImageInfo& info,
+               size_t rowBytes, uint64_t id)
         : SkPixelRef(info.width(), info.height(), address, rowBytes)
         , mInfo(validateAlpha(info))
-        , mPixelStorageType(PixelStorageType::Ashmem) {
+        , mPixelStorageType(PixelStorageType::Ashmem)
+        , mId(id != INVALID_BITMAP_ID ? id : getId(mPixelStorageType)) {
     mPixelStorage.ashmem.address = address;
     mPixelStorage.ashmem.fd = fd;
     mPixelStorage.ashmem.size = mappedSize;
@@ -293,7 +346,8 @@
         , mInfo(validateAlpha(info))
         , mPixelStorageType(PixelStorageType::Hardware)
         , mPalette(palette)
-        , mPaletteGenerationId(getGenerationID()) {
+        , mPaletteGenerationId(getGenerationID())
+        , mId(getId(mPixelStorageType)) {
     mPixelStorage.hardware.buffer = buffer;
     mPixelStorage.hardware.size = AHardwareBuffer_getAllocationSize(buffer);
     AHardwareBuffer_acquire(buffer);
@@ -578,6 +632,7 @@
 }
 
 std::mutex Bitmap::mLock{};
+
 size_t Bitmap::mTotalBitmapBytes = 0;
 size_t Bitmap::mTotalBitmapCount = 0;
 
diff --git a/libs/hwui/hwui/Bitmap.h b/libs/hwui/hwui/Bitmap.h
index 3d55d85..8abe6a8 100644
--- a/libs/hwui/hwui/Bitmap.h
+++ b/libs/hwui/hwui/Bitmap.h
@@ -37,10 +37,10 @@
 namespace android {
 
 enum class PixelStorageType {
-    WrappedPixelRef,
-    Heap,
-    Ashmem,
-    Hardware,
+    WrappedPixelRef = 0,
+    Heap = 1,
+    Ashmem = 2,
+    Hardware = 3,
 };
 
 // TODO: Find a better home for this. It's here because hwui/Bitmap is exported and CanvasTransform
@@ -79,6 +79,9 @@
     static sk_sp<Bitmap> allocateHeapBitmap(const SkImageInfo& info);
     static sk_sp<Bitmap> allocateHeapBitmap(size_t size, const SkImageInfo& i, size_t rowBytes);
 
+    static std::string getAshmemId(const char* tag, uint64_t bitmapId,
+                                   int width, int height, size_t size);
+
     /* The createFrom factories construct a new Bitmap object by wrapping the already allocated
      * memory that is provided as an input param.
      */
@@ -104,6 +107,10 @@
     void setColorSpace(sk_sp<SkColorSpace> colorSpace);
     void setAlphaType(SkAlphaType alphaType);
 
+    uint64_t getId() const {
+        return mId;
+    }
+
     void getSkBitmap(SkBitmap* outBitmap);
 
     SkBitmap getSkBitmap() {
@@ -177,11 +184,14 @@
   static bool compress(const SkBitmap& bitmap, JavaCompressFormat format,
                        int32_t quality, SkWStream* stream);
 private:
+    static constexpr uint64_t INVALID_BITMAP_ID = 0u;
+
     static sk_sp<Bitmap> allocateAshmemBitmap(size_t size, const SkImageInfo& i, size_t rowBytes);
 
     Bitmap(void* address, size_t allocSize, const SkImageInfo& info, size_t rowBytes);
     Bitmap(SkPixelRef& pixelRef, const SkImageInfo& info);
-    Bitmap(void* address, int fd, size_t mappedSize, const SkImageInfo& info, size_t rowBytes);
+    Bitmap(void* address, int fd, size_t mappedSize, const SkImageInfo& info, size_t rowBytes,
+           uint64_t id = INVALID_BITMAP_ID);
 #ifdef __ANDROID__ // Layoutlib does not support hardware acceleration
     Bitmap(AHardwareBuffer* buffer, const SkImageInfo& info, size_t rowBytes,
            BitmapPalette palette);
@@ -229,6 +239,9 @@
 
     sk_sp<SkImage> mImage;  // Cache is used only for HW Bitmaps with Skia pipeline.
 
+    uint64_t mId;                // unique ID for this bitmap
+    static uint64_t getId(PixelStorageType type);
+
     // for tracing total number and memory usage of bitmaps
     static std::mutex mLock;
     static size_t mTotalBitmapBytes;
diff --git a/libs/hwui/jni/Bitmap.cpp b/libs/hwui/jni/Bitmap.cpp
index 010c4e8..29efd98 100644
--- a/libs/hwui/jni/Bitmap.cpp
+++ b/libs/hwui/jni/Bitmap.cpp
@@ -196,7 +196,7 @@
         int density) {
     static jmethodID gBitmap_constructorMethodID =
         GetMethodIDOrDie(env, gBitmap_class,
-            "<init>", "(JIIIZ[BLandroid/graphics/NinePatch$InsetStruct;Z)V");
+            "<init>", "(JJIIIZ[BLandroid/graphics/NinePatch$InsetStruct;Z)V");
 
     bool isMutable = bitmapCreateFlags & kBitmapCreateFlag_Mutable;
     bool isPremultiplied = bitmapCreateFlags & kBitmapCreateFlag_Premultiplied;
@@ -209,7 +209,8 @@
         bitmapWrapper->bitmap().setImmutable();
     }
     jobject obj = env->NewObject(gBitmap_class, gBitmap_constructorMethodID,
-            reinterpret_cast<jlong>(bitmapWrapper), bitmap->width(), bitmap->height(), density,
+            static_cast<jlong>(bitmap->getId()), reinterpret_cast<jlong>(bitmapWrapper),
+            bitmap->width(), bitmap->height(), density,
             isPremultiplied, ninePatchChunk, ninePatchInsets, fromMalloc);
 
     if (env->ExceptionCheck() != 0) {
@@ -668,14 +669,20 @@
     return STATUS_OK;
 }
 
-static binder_status_t writeBlob(AParcel* parcel, const int32_t size, const void* data, bool immutable) {
+static binder_status_t writeBlob(AParcel* parcel, uint64_t bitmapId, const SkBitmap& bitmap) {
+    const size_t size = bitmap.computeByteSize();
+    const void* data = bitmap.getPixels();
+    const bool immutable = bitmap.isImmutable();
+
     if (size <= 0 || data == nullptr) {
         return STATUS_NOT_ENOUGH_DATA;
     }
     binder_status_t error = STATUS_OK;
     if (shouldUseAshmem(parcel, size)) {
         // Create new ashmem region with read/write priv
-        base::unique_fd fd(ashmem_create_region("bitmap", size));
+        auto ashmemId = Bitmap::getAshmemId("writeblob", bitmapId,
+                                            bitmap.width(), bitmap.height(), size);
+        base::unique_fd fd(ashmem_create_region(ashmemId.c_str(), size));
         if (fd.get() < 0) {
             return STATUS_NO_MEMORY;
         }
@@ -883,8 +890,7 @@
           p.allowFds() ? "allowed" : "forbidden");
 #endif
 
-    size_t size = bitmap.computeByteSize();
-    status = writeBlob(p.get(), size, bitmap.getPixels(), bitmap.isImmutable());
+    status = writeBlob(p.get(), bitmapWrapper->bitmap().getId(), bitmap);
     if (status) {
         doThrowRE(env, "Could not copy bitmap to parcel blob.");
         return JNI_FALSE;
diff --git a/lint-baseline.xml b/lint-baseline.xml
index 0320aab..8253c1f 100644
--- a/lint-baseline.xml
+++ b/lint-baseline.xml
@@ -11265,4 +11265,15 @@
             column="24"/>
     </issue>
 
-</issues>
\ No newline at end of file
+    <issue
+        id="SimpleManualPermissionEnforcement"
+        message="INetworkScoreCache permission check should be converted to @EnforcePermission annotation"
+        errorLine1="        mContext.enforceCallingOrSelfPermission(permission.DUMP, TAG);"
+        errorLine2="        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="frameworks/base/wifi/java/src/android/net/wifi/WifiNetworkScoreCache.java"
+            line="250"
+            column="9"/>
+    </issue>
+
+</issues>
diff --git a/location/java/android/location/flags/location.aconfig b/location/java/android/location/flags/location.aconfig
index c3cb492..24e1d32 100644
--- a/location/java/android/location/flags/location.aconfig
+++ b/location/java/android/location/flags/location.aconfig
@@ -141,3 +141,17 @@
         purpose: PURPOSE_BUGFIX
     }
 }
+
+flag {
+    name: "population_density_provider"
+    namespace: "location"
+    description: "Flag for enabling the population density provider"
+    bug: "376198890"
+}
+
+flag {
+    name: "density_based_coarse_locations"
+    namespace: "location"
+    description: "Flag for gating the density-based coarse locations"
+    bug: "376198890"
+}
diff --git a/media/java/android/media/AudioDeviceInfo.java b/media/java/android/media/AudioDeviceInfo.java
index 0efefb9..39b29d0 100644
--- a/media/java/android/media/AudioDeviceInfo.java
+++ b/media/java/android/media/AudioDeviceInfo.java
@@ -279,6 +279,7 @@
             TYPE_AUX_LINE,
             TYPE_IP,
             TYPE_BUS,
+            TYPE_REMOTE_SUBMIX,
             TYPE_HEARING_AID,
             TYPE_BUILTIN_SPEAKER_SAFE,
             TYPE_BLE_HEADSET,
@@ -312,6 +313,7 @@
             case TYPE_AUX_LINE:
             case TYPE_IP:
             case TYPE_BUS:
+            case TYPE_REMOTE_SUBMIX:
             case TYPE_HEARING_AID:
             case TYPE_BUILTIN_SPEAKER_SAFE:
             case TYPE_BLE_HEADSET:
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 536afd4..8250a53 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -896,6 +896,30 @@
      */
     public static final int USE_DEFAULT_STREAM_TYPE = Integer.MIN_VALUE;
 
+    /** @hide */
+    @IntDef(flag = false, prefix = "DEVICE_STATE", value = {
+            DEVICE_CONNECTION_STATE_DISCONNECTED,
+            DEVICE_CONNECTION_STATE_CONNECTED}
+            )
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface DeviceConnectionState {}
+
+    /**
+     * @hide The device connection state for disconnected devices.
+     */
+    @SystemApi
+    @SuppressLint("UnflaggedApi") // b/373465238
+    @RequiresPermission(Manifest.permission.MODIFY_AUDIO_ROUTING)
+    public static final int DEVICE_CONNECTION_STATE_DISCONNECTED = 0;
+
+    /**
+     * @hide The device connection state for connected devices.
+     */
+    @SystemApi
+    @SuppressLint("UnflaggedApi") // b/373465238
+    @RequiresPermission(Manifest.permission.MODIFY_AUDIO_ROUTING)
+    public static final int DEVICE_CONNECTION_STATE_CONNECTED = 1;
+
     private static IAudioService sService;
 
     /**
@@ -1137,7 +1161,7 @@
         final IAudioService service = getService();
         try {
             service.setMasterMute(mute, flags, getContext().getOpPackageName(),
-                    UserHandle.getCallingUserId(), getContext().getAttributionTag());
+                    getContext().getUserId(), getContext().getAttributionTag());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -3254,7 +3278,7 @@
         final IAudioService service = getService();
         try {
             service.setMicrophoneMute(on, getContext().getOpPackageName(),
-                    UserHandle.getCallingUserId(), getContext().getAttributionTag());
+                    getContext().getUserId(), getContext().getAttributionTag());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -6726,14 +6750,16 @@
     }
 
     /**
+     * @hide
      * Indicate wired accessory connection state change and attributes.
-     * @param state      new connection state: 1 connected, 0 disconnected
      * @param attributes attributes of the connected device
-     * {@hide}
+     * @param state      new connection state
      */
-    @UnsupportedAppUsage
+    @SystemApi
+    @SuppressLint("UnflaggedApi") // b/373465238
     @RequiresPermission(Manifest.permission.MODIFY_AUDIO_ROUTING)
-    public void setWiredDeviceConnectionState(AudioDeviceAttributes attributes, int state) {
+    public void setWiredDeviceConnectionState(@NonNull AudioDeviceAttributes attributes,
+            @DeviceConnectionState int state) {
         final IAudioService service = getService();
         try {
             service.setWiredDeviceConnectionState(attributes, state,
diff --git a/media/java/android/media/MediaMuxer.java b/media/java/android/media/MediaMuxer.java
index 5e55f64..678150b 100644
--- a/media/java/android/media/MediaMuxer.java
+++ b/media/java/android/media/MediaMuxer.java
@@ -584,45 +584,108 @@
      * The following table summarizes codec support for containers across android releases:
      *
      * <table>
-     *  <thead>
-     *   <tr>
-     *    <th rowspan=2>OS Version(s)</th>
-     *    <td colspan=3>Codec support</th>
-     *   </tr><tr>
-     *    <th>{@linkplain OutputFormat#MUXER_OUTPUT_MPEG_4 MP4}</th>
-     *    <th>{@linkplain OutputFormat#MUXER_OUTPUT_WEBM WEBM}</th>
-     *   </tr>
-     *  </thead>
-     *  <tbody>
-     *   <tr>
-     *    <td>{@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR2}</td>
-     *    <td rowspan=6>{@link MediaFormat#MIMETYPE_AUDIO_AAC AAC},<br>
-     *        {@link MediaFormat#MIMETYPE_AUDIO_AMR_NB NB-AMR},<br>
-     *        {@link MediaFormat#MIMETYPE_AUDIO_AMR_WB WB-AMR},<br>
-     *        {@link MediaFormat#MIMETYPE_VIDEO_H263 H.263},<br>
-     *        {@link MediaFormat#MIMETYPE_VIDEO_MPEG4 MPEG-4},<br>
-     *        {@link MediaFormat#MIMETYPE_VIDEO_AVC AVC} (H.264)</td>
-     *    <td rowspan=3>Not supported</td>
-     *   </tr><tr>
-     *    <td>{@link android.os.Build.VERSION_CODES#KITKAT}</td>
-     *   </tr><tr>
-     *    <td>{@link android.os.Build.VERSION_CODES#KITKAT_WATCH}</td>
-     *   </tr><tr>
-     *    <td>{@link android.os.Build.VERSION_CODES#LOLLIPOP}</td>
-     *    <td rowspan=3>{@link MediaFormat#MIMETYPE_AUDIO_VORBIS Vorbis},<br>
-     *        {@link MediaFormat#MIMETYPE_VIDEO_VP8 VP8}</td>
-     *   </tr><tr>
-     *    <td>{@link android.os.Build.VERSION_CODES#LOLLIPOP_MR1}</td>
-     *   </tr><tr>
-     *    <td>{@link android.os.Build.VERSION_CODES#M}</td>
-     *   </tr><tr>
-     *    <td>{@link android.os.Build.VERSION_CODES#N}</td>
-     *    <td>as above, plus<br>
-     *        {@link MediaFormat#MIMETYPE_VIDEO_HEVC HEVC} (H.265)</td>
-     *    <td>as above, plus<br>
-     *        {@link MediaFormat#MIMETYPE_VIDEO_VP9 VP9}</td>
-     *   </tr>
-     *  </tbody>
+     *   <thead>
+     *     <tr>
+     *       <th>Codec</th>
+     *       <th>{@linkplain OutputFormat#MUXER_OUTPUT_MPEG_4 MP4}</th>
+     *       <th>{@linkplain OutputFormat#MUXER_OUTPUT_WEBM WEBM}</th>
+     *       <th>{@linkplain OutputFormat#MUXER_OUTPUT_OGG OGG}</th>
+     *       <th>Supported From SDK version</th>
+     *     </tr>
+     *   </thead>
+     *   <tbody>
+     *     <tr>
+     *       <td>{@link MediaFormat#MIMETYPE_AUDIO_AAC AAC}</td>
+     *       <td>✓</td>
+     *       <td></td>
+     *       <td></td>
+     *       <td>17</td>
+     *     </tr>
+     *     <tr>
+     *       <td>{@link MediaFormat#MIMETYPE_AUDIO_AMR_NB NB-AMR}</td>
+     *       <td>✓</td>
+     *       <td></td>
+     *       <td></td>
+     *       <td>17</td>
+     *     </tr>
+     *     <tr>
+     *       <td>{@link MediaFormat#MIMETYPE_AUDIO_AMR_WB WB-AMR}</td>
+     *       <td>✓</td>
+     *       <td></td>
+     *       <td></td>
+     *       <td>17</td>
+     *     </tr>
+     *     <tr>
+     *       <td>{@link MediaFormat#MIMETYPE_VIDEO_H263 H.263}</td>
+     *       <td>✓</td>
+     *       <td></td>
+     *       <td></td>
+     *       <td>17</td>
+     *     </tr>
+     *     <tr>
+     *       <td>{@link MediaFormat#MIMETYPE_VIDEO_MPEG4 MPEG-4}</td>
+     *       <td>✓</td>
+     *       <td></td>
+     *       <td></td>
+     *       <td>17</td>
+     *     </tr>
+     *     <tr>
+     *       <td>{@link MediaFormat#MIMETYPE_VIDEO_AVC AVC} (H.264)</td>
+     *       <td>✓</td>
+     *       <td></td>
+     *       <td></td>
+     *       <td>17</td>
+     *     </tr>
+     *     <tr>
+     *       <td>{@link MediaFormat#MIMETYPE_AUDIO_VORBIS Vorbis}</td>
+     *       <td></td>
+     *       <td>✓</td>
+     *       <td></td>
+     *       <td>21</td>
+     *     </tr>
+     *     <tr>
+     *       <td>{@link MediaFormat#MIMETYPE_VIDEO_VP8 VP8}</td>
+     *       <td></td>
+     *       <td>✓</td>
+     *       <td></td>
+     *       <td>21</td>
+     *     </tr>
+     *     <tr>
+     *       <td>{@link MediaFormat#MIMETYPE_VIDEO_VP9 VP9}</td>
+     *       <td></td>
+     *       <td>✓</td>
+     *       <td></td>
+     *       <td>24</td>
+     *     </tr>
+     *     <tr>
+     *       <td>{@link MediaFormat#MIMETYPE_VIDEO_HEVC HEVC} (H.265)</td>
+     *       <td>✓</td>
+     *       <td></td>
+     *       <td></td>
+     *       <td>24</td>
+     *     </tr>
+     *     <tr>
+     *       <td>{@link MediaFormat#MIMETYPE_AUDIO_OPUS OPUS}</td>
+     *       <td></td>
+     *       <td>✓</td>
+     *       <td>✓</td>
+     *       <td>26</td>
+     *     </tr>
+     *     <tr>
+     *       <td>{@link MediaFormat#MIMETYPE_VIDEO_AV1 AV1}</td>
+     *       <td>✓</td>
+     *       <td></td>
+     *       <td></td>
+     *       <td>31</td>
+     *     </tr>
+     *     <tr>
+     *       <td>{@link MediaFormat#MIMETYPE_VIDEO_DOLBY_VISION Dolby Vision}</td>
+     *       <td>✓</td>
+     *       <td></td>
+     *       <td></td>
+     *       <td>32</td>
+     *     </tr>
+     *   </tbody>
      * </table>
      *
      * @param format The media format for the track.  This must not be an empty
diff --git a/media/java/android/media/audio/common/AidlConversion.java b/media/java/android/media/audio/common/AidlConversion.java
index 65fd51b..c1d73f9 100644
--- a/media/java/android/media/audio/common/AidlConversion.java
+++ b/media/java/android/media/audio/common/AidlConversion.java
@@ -753,9 +753,11 @@
                 break;
             case AudioSystem.DEVICE_IN_REMOTE_SUBMIX:
                 aidl.type = AudioDeviceType.IN_SUBMIX;
+                aidl.connection = AudioDeviceDescription.CONNECTION_VIRTUAL;
                 break;
             case AudioSystem.DEVICE_OUT_REMOTE_SUBMIX:
                 aidl.type = AudioDeviceType.OUT_SUBMIX;
+                aidl.connection = AudioDeviceDescription.CONNECTION_VIRTUAL;
                 break;
             case AudioSystem.DEVICE_IN_ANLG_DOCK_HEADSET:
                 aidl.type = AudioDeviceType.IN_DOCK;
diff --git a/media/java/android/media/audiopolicy/AudioPolicy.java b/media/java/android/media/audiopolicy/AudioPolicy.java
index 2c8e352..57f5f52c 100644
--- a/media/java/android/media/audiopolicy/AudioPolicy.java
+++ b/media/java/android/media/audiopolicy/AudioPolicy.java
@@ -316,7 +316,7 @@
         @NonNull
         public Builder setMediaProjection(@NonNull MediaProjection projection) {
             if (projection == null) {
-                throw new IllegalArgumentException("Invalid null volume callback");
+                throw new IllegalArgumentException("Invalid null media projection");
             }
             mProjection = projection;
             return this;
diff --git a/media/java/android/media/projection/IMediaProjection.aidl b/media/java/android/media/projection/IMediaProjection.aidl
index 7a1cf92..8ee966d 100644
--- a/media/java/android/media/projection/IMediaProjection.aidl
+++ b/media/java/android/media/projection/IMediaProjection.aidl
@@ -56,6 +56,13 @@
             + ".permission.MANAGE_MEDIA_PROJECTION)")
     int getTaskId();
 
+
+    /**
+     * Returns the displayId identifying the display to record. This only applies to full screen
+     * recording.
+     */
+    int getDisplayId();
+
     /**
      * Updates the {@link LaunchCookie} identifying the task to record.
      */
diff --git a/media/java/android/media/projection/IMediaProjectionManager.aidl b/media/java/android/media/projection/IMediaProjectionManager.aidl
index 3d927d3..b104972 100644
--- a/media/java/android/media/projection/IMediaProjectionManager.aidl
+++ b/media/java/android/media/projection/IMediaProjectionManager.aidl
@@ -46,14 +46,15 @@
     boolean hasProjectionPermission(int processUid, String packageName);
 
     /**
-     * Returns a new {@link IMediaProjection} instance associated with the given package.
+     * Returns a new {@link IMediaProjection} instance associated with the given package for the
+     * given display id.
      *
      * @param processUid the process UID as returned by {@link android.os.Process.myUid()}.
      */
     @JavaPassthrough(annotation = "@android.annotation.RequiresPermission(android.Manifest"
             + ".permission.MANAGE_MEDIA_PROJECTION)")
     IMediaProjection createProjection(int processUid, String packageName, int type,
-            boolean permanentGrant);
+            boolean permanentGrant, int displayId);
 
     /**
      * Returns the current {@link IMediaProjection} instance associated with the given
diff --git a/media/java/android/media/projection/MediaProjection.java b/media/java/android/media/projection/MediaProjection.java
index ef4c3ef..4114f53 100644
--- a/media/java/android/media/projection/MediaProjection.java
+++ b/media/java/android/media/projection/MediaProjection.java
@@ -18,6 +18,8 @@
 
 import static android.view.Display.DEFAULT_DISPLAY;
 
+import static com.android.media.projection.flags.Flags.mediaProjectionConnectedDisplay;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.compat.CompatChanges;
@@ -85,13 +87,23 @@
     public MediaProjection(Context context, IMediaProjection impl, DisplayManager displayManager) {
         mContext = context;
         mImpl = impl;
+        mDisplayManager = displayManager;
+
         try {
             mImpl.start(new MediaProjectionCallback());
+
+            if (mediaProjectionConnectedDisplay()) {
+                int displayId = mImpl.getDisplayId();
+                if (displayId != DEFAULT_DISPLAY) {
+                    mDisplayId = displayId;
+                    Log.v(TAG, "Created MediaProjection for display " + mDisplayId);
+                    return;
+                }
+            }
         } catch (RemoteException e) {
             Log.e(TAG, "Content Recording: Failed to start media projection", e);
             throw new RuntimeException("Failed to start media projection", e);
         }
-        mDisplayManager = displayManager;
 
         final UserManager userManager = context.getSystemService(UserManager.class);
         mDisplayId = userManager.isVisibleBackgroundUsersSupported()
diff --git a/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodRunnerState.java b/media/java/android/media/quality/IMediaQualityManager.aidl
similarity index 73%
copy from ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodRunnerState.java
copy to media/java/android/media/quality/IMediaQualityManager.aidl
index 83cbc52..4f1210a7 100644
--- a/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodRunnerState.java
+++ b/media/java/android/media/quality/IMediaQualityManager.aidl
@@ -13,10 +13,12 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package android.platform.test.ravenwood;
 
-/** Stub class. The actual implementaetion is in junit-impl-src. */
-public class RavenwoodRunnerState {
-    public RavenwoodRunnerState(RavenwoodAwareTestRunner runner) {
-    }
-}
+package android.media.quality;
+
+/**
+ * Interface for Media Quality Manager
+ * @hide
+ */
+interface IMediaQualityManager {
+}
\ No newline at end of file
diff --git a/media/java/android/media/quality/MediaQualityManager.java b/media/java/android/media/quality/MediaQualityManager.java
new file mode 100644
index 0000000..86dbcdb
--- /dev/null
+++ b/media/java/android/media/quality/MediaQualityManager.java
@@ -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 android.media.quality;
+
+import android.annotation.FlaggedApi;
+import android.annotation.SystemService;
+import android.content.Context;
+import android.media.tv.flags.Flags;
+
+/**
+ * Expose TV setting APIs for the application to use
+ * @hide
+ */
+@FlaggedApi(Flags.FLAG_MEDIA_QUALITY_FW)
+@SystemService(Context.MEDIA_QUALITY_SERVICE)
+public class MediaQualityManager {
+    // TODO: unhide the APIs for api review
+    private static final String TAG = "MediaQualityManager";
+
+    private final IMediaQualityManager mService;
+    private final Context mContext;
+
+    /**
+     * @hide
+     */
+    public MediaQualityManager(Context context, IMediaQualityManager service) {
+        mContext = context;
+        mService = service;
+    }
+}
diff --git a/media/java/android/media/quality/OWNERS b/media/java/android/media/quality/OWNERS
new file mode 100644
index 0000000..2f69027
--- /dev/null
+++ b/media/java/android/media/quality/OWNERS
@@ -0,0 +1 @@
+file:/services/core/java/com/android/server/media/quality/OWNERS
\ No newline at end of file
diff --git a/media/jni/android_media_ImageReader.cpp b/media/jni/android_media_ImageReader.cpp
index 019b1e0..effa92c 100644
--- a/media/jni/android_media_ImageReader.cpp
+++ b/media/jni/android_media_ImageReader.cpp
@@ -942,6 +942,11 @@
         return static_cast<jint>(PublicFormat::PRIVATE);
     } else {
         BufferItem* buffer = Image_getBufferItem(env, thiz);
+        if (buffer == nullptr) {
+            jniThrowException(env, "java/lang/IllegalStateException",
+                    "Image is not initialized");
+            return -1;
+        }
         int readerHalFormat = mapPublicFormatToHalFormat(static_cast<PublicFormat>(readerFormat));
         int32_t fmt = applyFormatOverrides(
                 buffer->mGraphicBuffer->getPixelFormat(), readerHalFormat);
@@ -960,6 +965,11 @@
 
 static jobject Image_getHardwareBuffer(JNIEnv* env, jobject thiz) {
     BufferItem* buffer = Image_getBufferItem(env, thiz);
+    if (buffer == nullptr) {
+        jniThrowException(env, "java/lang/IllegalStateException",
+                "Image is not initialized");
+        return NULL;
+    }
     AHardwareBuffer* b = AHardwareBuffer_from_GraphicBuffer(buffer->mGraphicBuffer.get());
     // don't user the public AHardwareBuffer_toHardwareBuffer() because this would force us
     // to link against libandroid.so
diff --git a/media/tests/projection/src/android/media/projection/FakeIMediaProjection.java b/media/tests/projection/src/android/media/projection/FakeIMediaProjection.java
index 6860c0b..c9807e6 100644
--- a/media/tests/projection/src/android/media/projection/FakeIMediaProjection.java
+++ b/media/tests/projection/src/android/media/projection/FakeIMediaProjection.java
@@ -22,6 +22,7 @@
 import android.app.ActivityOptions.LaunchCookie;
 import android.os.PermissionEnforcer;
 import android.os.RemoteException;
+import android.view.Display;
 
 /**
  * The connection between MediaProjection and system server is represented by IMediaProjection;
@@ -32,6 +33,7 @@
     boolean mIsStarted = false;
     LaunchCookie mLaunchCookie = null;
     IMediaProjectionCallback mIMediaProjectionCallback = null;
+    int mDisplayId = Display.DEFAULT_DISPLAY;
 
     FakeIMediaProjection(PermissionEnforcer enforcer) {
         super(enforcer);
@@ -93,6 +95,10 @@
         return mTaskId;
     }
 
+    public int getDisplayId() {
+        return mDisplayId;
+    }
+
     @Override
     @EnforcePermission(MANAGE_MEDIA_PROJECTION)
     public void setLaunchCookie(LaunchCookie launchCookie) throws RemoteException {
diff --git a/packages/CompanionDeviceManager/res/values-or/strings.xml b/packages/CompanionDeviceManager/res/values-or/strings.xml
index ffba68f..d431fb9 100644
--- a/packages/CompanionDeviceManager/res/values-or/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-or/strings.xml
@@ -53,7 +53,7 @@
     <string name="permission_calendar" msgid="6805668388691290395">"କେଲେଣ୍ଡର"</string>
     <string name="permission_microphone" msgid="2152206421428732949">"ମାଇକ୍ରୋଫୋନ"</string>
     <string name="permission_call_logs" msgid="5546761417694586041">"କଲ ଲଗଗୁଡ଼ିକ"</string>
-    <string name="permission_nearby_devices" msgid="7530973297737123481">"ଆଖପାଖର ଡିଭାଇସଗୁଡ଼ିକ"</string>
+    <string name="permission_nearby_devices" msgid="7530973297737123481">"ଆଖପାଖର ଡିଭାଇସ"</string>
     <string name="permission_media_routing_control" msgid="5498639511586715253">"ମିଡିଆ ଆଉଟପୁଟ ପରିବର୍ତ୍ତନ କରନ୍ତୁ"</string>
     <string name="permission_storage" msgid="6831099350839392343">"ଫଟୋ ଏବଂ ମିଡିଆ"</string>
     <string name="permission_notifications" msgid="4099418516590632909">"ବିଜ୍ଞପ୍ତିଗୁଡ଼ିକ"</string>
diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceDiscoveryService.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceDiscoveryService.java
index 6c1bc4e..f586e3d 100644
--- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceDiscoveryService.java
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceDiscoveryService.java
@@ -238,7 +238,12 @@
             // Cancel discovery.
             mBtAdapter.cancelDiscovery();
             // Unregister receiver.
-            unregisterReceiver(mBtReceiver);
+            try {
+                unregisterReceiver(mBtReceiver);
+            } catch (IllegalArgumentException e) {
+                Slog.e(TAG, "Unable to unregister BT receiver. The receiver is already"
+                        + " unregistered or was not previously registered.");
+            }
             mBtReceiver = null;
         }
 
diff --git a/packages/CrashRecovery/framework/Android.bp b/packages/CrashRecovery/framework/Android.bp
index 1be776d..2beffda 100644
--- a/packages/CrashRecovery/framework/Android.bp
+++ b/packages/CrashRecovery/framework/Android.bp
@@ -6,7 +6,25 @@
     ],
     path: "java",
     visibility: [
-        "//frameworks/base:__subpackages__",
         "//packages/modules/CrashRecovery/framework",
     ],
 }
+
+java_sdk_library {
+    name: "framework-platformcrashrecovery",
+    srcs: [":framework-crashrecovery-sources"],
+    defaults: ["framework-non-updatable-unbundled-defaults"],
+    aidl: {
+        include_dirs: [
+            "frameworks/base/core/java",
+        ],
+    },
+    impl_library_visibility: [
+        "//frameworks/base:__subpackages__",
+    ],
+}
+
+platform_compat_config {
+    name: "framework-platformcrashrecovery-compat-config",
+    src: ":framework-platformcrashrecovery",
+}
diff --git a/packages/CrashRecovery/framework/api/current.txt b/packages/CrashRecovery/framework/api/current.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/packages/CrashRecovery/framework/api/current.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/packages/CrashRecovery/framework/api/module-lib-current.txt b/packages/CrashRecovery/framework/api/module-lib-current.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/packages/CrashRecovery/framework/api/module-lib-current.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/packages/CrashRecovery/framework/api/module-lib-removed.txt b/packages/CrashRecovery/framework/api/module-lib-removed.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/packages/CrashRecovery/framework/api/module-lib-removed.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/packages/CrashRecovery/framework/api/removed.txt b/packages/CrashRecovery/framework/api/removed.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/packages/CrashRecovery/framework/api/removed.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/packages/CrashRecovery/framework/api/system-current.txt b/packages/CrashRecovery/framework/api/system-current.txt
new file mode 100644
index 0000000..3a48a4a
--- /dev/null
+++ b/packages/CrashRecovery/framework/api/system-current.txt
@@ -0,0 +1,26 @@
+// Signature format: 2.0
+package android.service.watchdog {
+
+  public abstract class ExplicitHealthCheckService extends android.app.Service {
+    ctor public ExplicitHealthCheckService();
+    method public final void notifyHealthCheckPassed(@NonNull String);
+    method @NonNull public final android.os.IBinder onBind(@NonNull android.content.Intent);
+    method public abstract void onCancelHealthCheck(@NonNull String);
+    method @NonNull public abstract java.util.List<java.lang.String> onGetRequestedPackages();
+    method @NonNull public abstract java.util.List<android.service.watchdog.ExplicitHealthCheckService.PackageConfig> onGetSupportedPackages();
+    method public abstract void onRequestHealthCheck(@NonNull String);
+    field public static final String BIND_PERMISSION = "android.permission.BIND_EXPLICIT_HEALTH_CHECK_SERVICE";
+    field public static final String SERVICE_INTERFACE = "android.service.watchdog.ExplicitHealthCheckService";
+  }
+
+  public static final class ExplicitHealthCheckService.PackageConfig implements android.os.Parcelable {
+    ctor public ExplicitHealthCheckService.PackageConfig(@NonNull String, long);
+    method public int describeContents();
+    method public long getHealthCheckTimeoutMillis();
+    method @NonNull public String getPackageName();
+    method public void writeToParcel(android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.service.watchdog.ExplicitHealthCheckService.PackageConfig> CREATOR;
+  }
+
+}
+
diff --git a/packages/CrashRecovery/framework/api/system-lint-baseline.txt b/packages/CrashRecovery/framework/api/system-lint-baseline.txt
new file mode 100644
index 0000000..f52be7c
--- /dev/null
+++ b/packages/CrashRecovery/framework/api/system-lint-baseline.txt
@@ -0,0 +1,47 @@
+// Baseline format: 1.0
+InvalidNullabilityOverride: android.service.watchdog.ExplicitHealthCheckService#onBind(android.content.Intent):
+    Invalid nullability on type android.content.Intent in parameter `intent` in method `onBind`. Parameter in method override cannot use a non-null type when the corresponding type from the super method is platform-nullness.
+InvalidNullabilityOverride: android.service.watchdog.ExplicitHealthCheckService#onBind(android.content.Intent) parameter #0:
+    Invalid nullability on type android.content.Intent in parameter `intent` in method `onBind`. Parameter in method override cannot use a non-null type when the corresponding type from the super method is platform-nullness.
+
+
+MissingNullability: android.service.watchdog.ExplicitHealthCheckService.PackageConfig#writeToParcel(android.os.Parcel,int):
+    Missing nullability on parameter `parcel` in method `writeToParcel`
+
+
+UnflaggedApi: android.service.watchdog.ExplicitHealthCheckService:
+    New API must be flagged with @FlaggedApi: class android.service.watchdog.ExplicitHealthCheckService
+UnflaggedApi: android.service.watchdog.ExplicitHealthCheckService#BIND_PERMISSION:
+    New API must be flagged with @FlaggedApi: field android.service.watchdog.ExplicitHealthCheckService.BIND_PERMISSION
+UnflaggedApi: android.service.watchdog.ExplicitHealthCheckService#SERVICE_INTERFACE:
+    New API must be flagged with @FlaggedApi: field android.service.watchdog.ExplicitHealthCheckService.SERVICE_INTERFACE
+UnflaggedApi: android.service.watchdog.ExplicitHealthCheckService#notifyHealthCheckPassed(String):
+    New API must be flagged with @FlaggedApi: method android.service.watchdog.ExplicitHealthCheckService.notifyHealthCheckPassed(String)
+UnflaggedApi: android.service.watchdog.ExplicitHealthCheckService#onBind(android.content.Intent):
+    New API must be flagged with @FlaggedApi: method android.service.watchdog.ExplicitHealthCheckService.onBind(android.content.Intent)
+UnflaggedApi: android.service.watchdog.ExplicitHealthCheckService#onCancelHealthCheck(String):
+    New API must be flagged with @FlaggedApi: method android.service.watchdog.ExplicitHealthCheckService.onCancelHealthCheck(String)
+UnflaggedApi: android.service.watchdog.ExplicitHealthCheckService#onGetRequestedPackages():
+    New API must be flagged with @FlaggedApi: method android.service.watchdog.ExplicitHealthCheckService.onGetRequestedPackages()
+UnflaggedApi: android.service.watchdog.ExplicitHealthCheckService#onGetSupportedPackages():
+    New API must be flagged with @FlaggedApi: method android.service.watchdog.ExplicitHealthCheckService.onGetSupportedPackages()
+UnflaggedApi: android.service.watchdog.ExplicitHealthCheckService#onRequestHealthCheck(String):
+    New API must be flagged with @FlaggedApi: method android.service.watchdog.ExplicitHealthCheckService.onRequestHealthCheck(String)
+UnflaggedApi: android.service.watchdog.ExplicitHealthCheckService.PackageConfig:
+    New API must be flagged with @FlaggedApi: class android.service.watchdog.ExplicitHealthCheckService.PackageConfig
+UnflaggedApi: android.service.watchdog.ExplicitHealthCheckService.PackageConfig#CREATOR:
+    New API must be flagged with @FlaggedApi: field android.service.watchdog.ExplicitHealthCheckService.PackageConfig.CREATOR
+UnflaggedApi: android.service.watchdog.ExplicitHealthCheckService.PackageConfig#PackageConfig(String, long):
+    New API must be flagged with @FlaggedApi: constructor android.service.watchdog.ExplicitHealthCheckService.PackageConfig(String,long)
+UnflaggedApi: android.service.watchdog.ExplicitHealthCheckService.PackageConfig#PackageConfig(String,long):
+    New API must be flagged with @FlaggedApi: constructor android.service.watchdog.ExplicitHealthCheckService.PackageConfig(String,long)
+UnflaggedApi: android.service.watchdog.ExplicitHealthCheckService.PackageConfig#describeContents():
+    New API must be flagged with @FlaggedApi: method android.service.watchdog.ExplicitHealthCheckService.PackageConfig.describeContents()
+UnflaggedApi: android.service.watchdog.ExplicitHealthCheckService.PackageConfig#getHealthCheckTimeoutMillis():
+    New API must be flagged with @FlaggedApi: method android.service.watchdog.ExplicitHealthCheckService.PackageConfig.getHealthCheckTimeoutMillis()
+UnflaggedApi: android.service.watchdog.ExplicitHealthCheckService.PackageConfig#getPackageName():
+    New API must be flagged with @FlaggedApi: method android.service.watchdog.ExplicitHealthCheckService.PackageConfig.getPackageName()
+UnflaggedApi: android.service.watchdog.ExplicitHealthCheckService.PackageConfig#writeToParcel(android.os.Parcel, int):
+    New API must be flagged with @FlaggedApi: method android.service.watchdog.ExplicitHealthCheckService.PackageConfig.writeToParcel(android.os.Parcel,int)
+UnflaggedApi: android.service.watchdog.ExplicitHealthCheckService.PackageConfig#writeToParcel(android.os.Parcel,int):
+    New API must be flagged with @FlaggedApi: method android.service.watchdog.ExplicitHealthCheckService.PackageConfig.writeToParcel(android.os.Parcel,int)
\ No newline at end of file
diff --git a/packages/CrashRecovery/framework/api/system-removed.txt b/packages/CrashRecovery/framework/api/system-removed.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/packages/CrashRecovery/framework/api/system-removed.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/packages/CrashRecovery/framework/api/test-current.txt b/packages/CrashRecovery/framework/api/test-current.txt
new file mode 100644
index 0000000..54f501f
--- /dev/null
+++ b/packages/CrashRecovery/framework/api/test-current.txt
@@ -0,0 +1,9 @@
+// Signature format: 2.0
+package android.service.watchdog {
+
+  public abstract class ExplicitHealthCheckService extends android.app.Service {
+    method public void setCallback(@Nullable android.os.RemoteCallback);
+  }
+
+}
+
diff --git a/packages/CrashRecovery/framework/api/test-removed.txt b/packages/CrashRecovery/framework/api/test-removed.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/packages/CrashRecovery/framework/api/test-removed.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/packages/CrashRecovery/services/module/java/com/android/server/PackageWatchdog.java b/packages/CrashRecovery/services/module/java/com/android/server/PackageWatchdog.java
index 8e5ae20..fbf51fd 100644
--- a/packages/CrashRecovery/services/module/java/com/android/server/PackageWatchdog.java
+++ b/packages/CrashRecovery/services/module/java/com/android/server/PackageWatchdog.java
@@ -19,6 +19,7 @@
 import static android.content.Intent.ACTION_REBOOT;
 import static android.content.Intent.ACTION_SHUTDOWN;
 import static android.service.watchdog.ExplicitHealthCheckService.PackageConfig;
+import static android.util.Xml.Encoding.UTF_8;
 
 import static com.android.server.crashrecovery.CrashRecoveryUtils.dumpCrashRecoveryEvents;
 
@@ -58,13 +59,14 @@
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.FastXmlSerializer;
 import com.android.modules.utils.BackgroundThread;
-import com.android.modules.utils.TypedXmlPullParser;
-import com.android.modules.utils.TypedXmlSerializer;
 
 import libcore.io.IoUtils;
 
+import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
 
 import java.io.BufferedReader;
 import java.io.BufferedWriter;
@@ -1152,7 +1154,8 @@
         mAllObservers.clear();
         try {
             infile = mPolicyFile.openRead();
-            final TypedXmlPullParser parser = Xml.resolvePullParser(infile);
+            final XmlPullParser parser = Xml.newPullParser();
+            parser.setInput(infile, UTF_8.name());
             XmlUtils.beginDocument(parser, TAG_PACKAGE_WATCHDOG);
             int outerDepth = parser.getDepth();
             while (XmlUtils.nextElementWithin(parser, outerDepth)) {
@@ -1163,7 +1166,7 @@
             }
         } catch (FileNotFoundException e) {
             // Nothing to monitor
-        } catch (IOException | NumberFormatException | XmlPullParserException e) {
+        } catch (Exception e) {
             Slog.wtf(TAG, "Unable to read monitored packages, deleting file", e);
             mPolicyFile.delete();
         } finally {
@@ -1237,10 +1240,11 @@
             }
 
             try {
-                TypedXmlSerializer out = Xml.resolveSerializer(stream);
+                XmlSerializer out = new FastXmlSerializer();
+                out.setOutput(stream, UTF_8.name());
                 out.startDocument(null, true);
                 out.startTag(null, TAG_PACKAGE_WATCHDOG);
-                out.attributeInt(null, ATTR_VERSION, DB_VERSION);
+                out.attribute(null, ATTR_VERSION, Integer.toString(DB_VERSION));
                 for (int oIndex = 0; oIndex < mAllObservers.size(); oIndex++) {
                     mAllObservers.valueAt(oIndex).writeLocked(out);
                 }
@@ -1356,12 +1360,12 @@
          * Does not persist any package failure thresholds.
          */
         @GuardedBy("mLock")
-        public boolean writeLocked(TypedXmlSerializer out) {
+        public boolean writeLocked(XmlSerializer out) {
             try {
                 out.startTag(null, TAG_OBSERVER);
                 out.attribute(null, ATTR_NAME, name);
                 if (Flags.recoverabilityDetection()) {
-                    out.attributeInt(null, ATTR_MITIGATION_COUNT, mMitigationCount);
+                    out.attribute(null, ATTR_MITIGATION_COUNT, Integer.toString(mMitigationCount));
                 }
                 for (int i = 0; i < mPackages.size(); i++) {
                     MonitoredPackage p = mPackages.valueAt(i);
@@ -1486,7 +1490,7 @@
          * #loadFromFile which in turn is only called on construction of the
          * singleton PackageWatchdog.
          **/
-        public static ObserverInternal read(TypedXmlPullParser parser, PackageWatchdog watchdog) {
+        public static ObserverInternal read(XmlPullParser parser, PackageWatchdog watchdog) {
             String observerName = null;
             int observerMitigationCount = 0;
             if (TAG_OBSERVER.equals(parser.getName())) {
@@ -1501,9 +1505,9 @@
             try {
                 if (Flags.recoverabilityDetection()) {
                     try {
-                        observerMitigationCount =
-                                parser.getAttributeInt(null, ATTR_MITIGATION_COUNT);
-                    } catch (XmlPullParserException e) {
+                        observerMitigationCount = Integer.parseInt(
+                                parser.getAttributeValue(null, ATTR_MITIGATION_COUNT));
+                    } catch (Exception e) {
                         Slog.i(
                             TAG,
                             "ObserverInternal mitigation count was not present.");
@@ -1579,13 +1583,14 @@
                 hasPassedHealthCheck, mitigationCalls);
     }
 
-    MonitoredPackage parseMonitoredPackage(TypedXmlPullParser parser)
+    MonitoredPackage parseMonitoredPackage(XmlPullParser parser)
             throws XmlPullParserException {
         String packageName = parser.getAttributeValue(null, ATTR_NAME);
-        long duration = parser.getAttributeLong(null, ATTR_DURATION);
-        long healthCheckDuration = parser.getAttributeLong(null,
-                        ATTR_EXPLICIT_HEALTH_CHECK_DURATION);
-        boolean hasPassedHealthCheck = parser.getAttributeBoolean(null, ATTR_PASSED_HEALTH_CHECK);
+        long duration = Long.parseLong(parser.getAttributeValue(null, ATTR_DURATION));
+        long healthCheckDuration = Long.parseLong(parser.getAttributeValue(null,
+                ATTR_EXPLICIT_HEALTH_CHECK_DURATION));
+        boolean hasPassedHealthCheck = Boolean.parseBoolean(parser.getAttributeValue(null,
+                ATTR_PASSED_HEALTH_CHECK));
         LongArrayQueue mitigationCalls = parseLongArrayQueue(
                 parser.getAttributeValue(null, ATTR_MITIGATION_CALLS));
         return newMonitoredPackage(packageName,
@@ -1643,12 +1648,13 @@
          * @hide
          */
         @GuardedBy("mLock")
-        public void writeLocked(TypedXmlSerializer out) throws IOException {
+        public void writeLocked(XmlSerializer out) throws IOException {
             out.startTag(null, TAG_PACKAGE);
             out.attribute(null, ATTR_NAME, getName());
-            out.attributeLong(null, ATTR_DURATION, mDurationMs);
-            out.attributeLong(null, ATTR_EXPLICIT_HEALTH_CHECK_DURATION, mHealthCheckDurationMs);
-            out.attributeBoolean(null, ATTR_PASSED_HEALTH_CHECK, mHasPassedHealthCheck);
+            out.attribute(null, ATTR_DURATION, Long.toString(mDurationMs));
+            out.attribute(null, ATTR_EXPLICIT_HEALTH_CHECK_DURATION,
+                    Long.toString(mHealthCheckDurationMs));
+            out.attribute(null, ATTR_PASSED_HEALTH_CHECK, Boolean.toString(mHasPassedHealthCheck));
             LongArrayQueue normalizedCalls = normalizeMitigationCalls();
             out.attribute(null, ATTR_MITIGATION_CALLS, longArrayQueueToString(normalizedCalls));
             out.endTag(null, TAG_PACKAGE);
diff --git a/packages/PrintSpooler/res/values-ne/strings.xml b/packages/PrintSpooler/res/values-ne/strings.xml
index 13c3886..b9b49ce 100644
--- a/packages/PrintSpooler/res/values-ne/strings.xml
+++ b/packages/PrintSpooler/res/values-ne/strings.xml
@@ -52,7 +52,7 @@
     <string name="add_print_service_label" msgid="5356702546188981940">"सेवा हाल्नुहोस्"</string>
     <string name="print_search_box_shown_utterance" msgid="7967404953901376090">"खोज बाकस देखाइयो"</string>
     <string name="print_search_box_hidden_utterance" msgid="5727755169343113351">"खोज बाकस लुकाइयो"</string>
-    <string name="print_add_printer" msgid="1088656468360653455">"प्रिन्टर थप्नुहोस्"</string>
+    <string name="print_add_printer" msgid="1088656468360653455">"प्रिन्टर कनेक्ट गर्नुहोस्"</string>
     <string name="print_select_printer" msgid="7388760939873368698">"प्रिन्टर चयन गर्नुहोस्"</string>
     <string name="print_forget_printer" msgid="5035287497291910766">"प्रिन्टर बिर्सनुहोस्"</string>
     <plurals name="print_search_result_count_utterance" formatted="false" msgid="6997663738361080868">
diff --git a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/HandlerExecutor.kt b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/HandlerExecutor.kt
new file mode 100644
index 0000000..be08606
--- /dev/null
+++ b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/HandlerExecutor.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.settingslib.datastore
+
+import android.os.Handler
+import android.os.Looper
+import java.util.concurrent.Executor
+
+/**
+ * Adapter of [Handler] and [Executor], where the task is executed on handler with given looper.
+ *
+ * When current looper is same with the given looper, task passed to [Executor.execute] will be
+ * executed immediately to improve better performance.
+ *
+ * @param looper Looper of the handler.
+ */
+open class HandlerExecutor(looper: Looper) : Handler(looper), Executor {
+
+    override fun execute(command: Runnable) {
+        if (looper == Looper.myLooper()) {
+            command.run()
+        } else {
+            post(command)
+        }
+    }
+
+    companion object {
+        /** The main thread [HandlerExecutor]. */
+        val main = HandlerExecutor(Looper.getMainLooper())
+    }
+}
diff --git a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/SettingsStore.kt b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/SettingsStore.kt
index 62d3fc3..04d4bfe 100644
--- a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/SettingsStore.kt
+++ b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/SettingsStore.kt
@@ -19,8 +19,6 @@
 import android.content.ContentResolver
 import android.database.ContentObserver
 import android.net.Uri
-import android.os.Handler
-import android.os.Looper
 import android.util.Log
 import java.util.concurrent.Executor
 import java.util.concurrent.atomic.AtomicInteger
@@ -39,7 +37,7 @@
     private val counter = AtomicInteger()
 
     private val contentObserver =
-        object : ContentObserver(Handler(Looper.getMainLooper())) {
+        object : ContentObserver(HandlerExecutor.main) {
             override fun onChange(selfChange: Boolean) {
                 super.onChange(selfChange, null)
             }
diff --git a/packages/SettingsLib/Graph/src/com/android/settingslib/graph/GetPreferenceGraphApiHandler.kt b/packages/SettingsLib/Graph/src/com/android/settingslib/graph/GetPreferenceGraphApiHandler.kt
index 04c2968..fdffe5d 100644
--- a/packages/SettingsLib/Graph/src/com/android/settingslib/graph/GetPreferenceGraphApiHandler.kt
+++ b/packages/SettingsLib/Graph/src/com/android/settingslib/graph/GetPreferenceGraphApiHandler.kt
@@ -21,10 +21,11 @@
 import com.android.settingslib.graph.proto.PreferenceGraphProto
 import com.android.settingslib.ipc.ApiHandler
 import com.android.settingslib.ipc.MessageCodec
+import com.android.settingslib.metadata.PreferenceScreenRegistry
 import java.util.Locale
 
 /** API to get preference graph. */
-abstract class GetPreferenceGraphApiHandler(private val activityClasses: Set<String>) :
+abstract class GetPreferenceGraphApiHandler :
     ApiHandler<GetPreferenceGraphRequest, PreferenceGraphProto> {
 
     override val requestCodec: MessageCodec<GetPreferenceGraphRequest>
@@ -40,8 +41,9 @@
         request: GetPreferenceGraphRequest,
     ): PreferenceGraphProto {
         val builderRequest =
-            if (request.activityClasses.isEmpty()) {
-                GetPreferenceGraphRequest(activityClasses, request.visitedScreens, request.locale)
+            if (request.screenKeys.isEmpty()) {
+                val keys = PreferenceScreenRegistry.preferenceScreens.keys
+                GetPreferenceGraphRequest(keys, request.visitedScreens, request.locale)
             } else {
                 request
             }
@@ -52,14 +54,14 @@
 /**
  * Request of [GetPreferenceGraphApiHandler].
  *
- * @param activityClasses activities of the preference graph
+ * @param screenKeys screen keys of the preference graph
  * @param visitedScreens keys of the visited preference screen
  * @param locale locale of the preference graph
  */
 data class GetPreferenceGraphRequest
 @JvmOverloads
 constructor(
-    val activityClasses: Set<String> = setOf(),
+    val screenKeys: Set<String> = setOf(),
     val visitedScreens: Set<String> = setOf(),
     val locale: Locale? = null,
     val includeValue: Boolean = true,
@@ -68,25 +70,25 @@
 object GetPreferenceGraphRequestCodec : MessageCodec<GetPreferenceGraphRequest> {
     override fun encode(data: GetPreferenceGraphRequest): Bundle =
         Bundle(3).apply {
-            putStringArray(KEY_ACTIVITIES, data.activityClasses.toTypedArray())
-            putStringArray(KEY_PREF_KEYS, data.visitedScreens.toTypedArray())
+            putStringArray(KEY_SCREEN_KEYS, data.screenKeys.toTypedArray())
+            putStringArray(KEY_VISITED_KEYS, data.visitedScreens.toTypedArray())
             putString(KEY_LOCALE, data.locale?.toLanguageTag())
         }
 
     override fun decode(data: Bundle): GetPreferenceGraphRequest {
-        val activities = data.getStringArray(KEY_ACTIVITIES) ?: arrayOf()
-        val visitedScreens = data.getStringArray(KEY_PREF_KEYS) ?: arrayOf()
+        val screenKeys = data.getStringArray(KEY_SCREEN_KEYS) ?: arrayOf()
+        val visitedScreens = data.getStringArray(KEY_VISITED_KEYS) ?: arrayOf()
         fun String?.toLocale() = if (this != null) Locale.forLanguageTag(this) else null
         return GetPreferenceGraphRequest(
-            activities.toSet(),
+            screenKeys.toSet(),
             visitedScreens.toSet(),
             data.getString(KEY_LOCALE).toLocale(),
         )
     }
 
-    private const val KEY_ACTIVITIES = "activities"
-    private const val KEY_PREF_KEYS = "keys"
-    private const val KEY_LOCALE = "locale"
+    private const val KEY_SCREEN_KEYS = "k"
+    private const val KEY_VISITED_KEYS = "v"
+    private const val KEY_LOCALE = "l"
 }
 
 object PreferenceGraphProtoCodec : MessageCodec<PreferenceGraphProto> {
@@ -96,5 +98,5 @@
     override fun decode(data: Bundle): PreferenceGraphProto =
         PreferenceGraphProto.parseFrom(data.getByteArray(KEY_GRAPH)!!)
 
-    private const val KEY_GRAPH = "graph"
+    private const val KEY_GRAPH = "g"
 }
diff --git a/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceGraphBuilder.kt b/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceGraphBuilder.kt
index 8fb16d8..5e78a29 100644
--- a/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceGraphBuilder.kt
+++ b/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceGraphBuilder.kt
@@ -19,14 +19,12 @@
 package com.android.settingslib.graph
 
 import android.annotation.SuppressLint
-import android.app.Activity
 import android.content.Context
 import android.content.Intent
 import android.content.pm.PackageManager
 import android.content.res.Configuration
 import android.os.Build
 import android.os.Bundle
-import android.preference.PreferenceActivity
 import android.util.Log
 import androidx.fragment.app.Fragment
 import androidx.preference.Preference
@@ -53,18 +51,13 @@
 import com.android.settingslib.metadata.PreferenceTitleProvider
 import com.android.settingslib.preference.PreferenceScreenFactory
 import com.android.settingslib.preference.PreferenceScreenProvider
-import java.util.Locale
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.withContext
+import java.util.Locale
 
 private const val TAG = "PreferenceGraphBuilder"
 
-/**
- * Builder of preference graph.
- *
- * Only activity in current application is supported. To create preference graph across
- * applications, use [crawlPreferenceGraph].
- */
+/** Builder of preference graph. */
 class PreferenceGraphBuilder
 private constructor(private val context: Context, private val request: GetPreferenceGraphRequest) {
     private val preferenceScreenFactory by lazy {
@@ -75,21 +68,13 @@
     private val includeValue = request.includeValue
 
     private suspend fun init() {
-        for (activityClass in request.activityClasses) {
-            add(activityClass)
-        }
-        // Temporarily add all screens
-        for (key in PreferenceScreenRegistry.preferenceScreens.keys) {
-            addPreferenceScreenFromRegistry(key, Activity::class.java)
+        for (key in request.screenKeys) {
+            addPreferenceScreenFromRegistry(key)
         }
     }
 
     fun build() = builder.build()
 
-    /** Adds an activity to the graph. */
-    suspend fun <T> add(activityClass: Class<T>) where T : Activity, T : PreferenceScreenProvider =
-        addPreferenceScreenProvider(activityClass)
-
     /**
      * Adds an activity to the graph.
      *
@@ -100,8 +85,10 @@
         try {
             val intent = Intent()
             intent.setClassName(context, activityClassName)
-            if (context.packageManager.resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY) ==
-                null) {
+            if (
+                context.packageManager.resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY) ==
+                    null
+            ) {
                 Log.e(TAG, "$activityClassName is not activity")
                 return
             }
@@ -122,7 +109,7 @@
             return false
         }
         val key = getPreferenceScreenKey { activityClass.newInstance() } ?: return false
-        if (addPreferenceScreenFromRegistry(key, activityClass)) {
+        if (addPreferenceScreenFromRegistry(key)) {
             builder.addRoots(key)
             return true
         }
@@ -144,23 +131,16 @@
             null
         }
 
-    private suspend fun addPreferenceScreenFromRegistry(
-        key: String,
-        activityClass: Class<*>,
-    ): Boolean {
+    private suspend fun addPreferenceScreenFromRegistry(key: String): Boolean {
         val metadata = PreferenceScreenRegistry[key] ?: return false
-        if (!metadata.hasCompleteHierarchy()) return false
-        return addPreferenceScreenMetadata(metadata, activityClass)
+        return addPreferenceScreenMetadata(metadata)
     }
 
-    private suspend fun addPreferenceScreenMetadata(
-        metadata: PreferenceScreenMetadata,
-        activityClass: Class<*>,
-    ): Boolean =
-        addPreferenceScreen(metadata.key, activityClass) {
+    private suspend fun addPreferenceScreenMetadata(metadata: PreferenceScreenMetadata): Boolean =
+        addPreferenceScreen(metadata.key) {
             preferenceScreenProto {
-                completeHierarchy = true
-                root = metadata.getPreferenceHierarchy(context).toProto(activityClass, true)
+                completeHierarchy = metadata.hasCompleteHierarchy()
+                root = metadata.getPreferenceHierarchy(context).toProto(true)
             }
         }
 
@@ -168,7 +148,7 @@
         Log.d(TAG, "add $activityClass")
         createPreferenceScreen { activityClass.newInstance() }
             ?.let {
-                addPreferenceScreen(Intent(context, activityClass), activityClass, it)
+                addPreferenceScreen(Intent(context, activityClass), it)
                 builder.addRoots(it.key)
             }
     }
@@ -195,106 +175,85 @@
             return@withContext null
         }
 
-    private suspend fun addPreferenceScreen(
-        intent: Intent,
-        activityClass: Class<*>,
-        preferenceScreen: PreferenceScreen?,
-    ) {
+    private suspend fun addPreferenceScreen(intent: Intent, preferenceScreen: PreferenceScreen?) {
         val key = preferenceScreen?.key
         if (key.isNullOrEmpty()) {
-            Log.e(TAG, "$activityClass \"$preferenceScreen\" has no key")
+            Log.e(TAG, "\"$preferenceScreen\" has no key")
             return
         }
-        @Suppress("CheckReturnValue")
-        addPreferenceScreen(key, activityClass) { preferenceScreen.toProto(intent, activityClass) }
+        @Suppress("CheckReturnValue") addPreferenceScreen(key) { preferenceScreen.toProto(intent) }
     }
 
     private suspend fun addPreferenceScreen(
         key: String,
-        activityClass: Class<*>,
         preferenceScreenProvider: suspend () -> PreferenceScreenProto,
-    ): Boolean {
-        if (!visitedScreens.add(key)) {
-            Log.w(TAG, "$activityClass $key visited")
-            return false
-        }
-        val activityClassName = activityClass.name
-        val associatedKey = builder.getActivityScreensOrDefault(activityClassName, null)
-        if (associatedKey == null) {
-            builder.putActivityScreens(activityClassName, key)
-        } else if (associatedKey != key) {
-            Log.w(TAG, "Dup $activityClassName association, old: $associatedKey, new: $key")
-        }
-        builder.putScreens(key, preferenceScreenProvider())
-        return true
-    }
-
-    private suspend fun PreferenceScreen.toProto(
-        intent: Intent,
-        activityClass: Class<*>,
-    ): PreferenceScreenProto = preferenceScreenProto {
-        this.intent = intent.toProto()
-        root = (this@toProto as PreferenceGroup).toProto(activityClass)
-    }
-
-    private suspend fun PreferenceGroup.toProto(activityClass: Class<*>): PreferenceGroupProto =
-        preferenceGroupProto {
-            preference = (this@toProto as Preference).toProto(activityClass)
-            for (index in 0 until preferenceCount) {
-                val child = getPreference(index)
-                addPreferences(
-                    preferenceOrGroupProto {
-                        if (child is PreferenceGroup) {
-                            group = child.toProto(activityClass)
-                        } else {
-                            preference = child.toProto(activityClass)
-                        }
-                    })
-            }
+    ): Boolean =
+        if (visitedScreens.add(key)) {
+            builder.putScreens(key, preferenceScreenProvider())
+            true
+        } else {
+            Log.w(TAG, "$key visited")
+            false
         }
 
-    private suspend fun Preference.toProto(activityClass: Class<*>): PreferenceProto =
-        preferenceProto {
-            this@toProto.key?.let { key = it }
-            this@toProto.title?.let { title = textProto { string = it.toString() } }
-            this@toProto.summary?.let { summary = textProto { string = it.toString() } }
-            val preferenceExtras = peekExtras()
-            preferenceExtras?.let { extras = it.toProto() }
-            enabled = isEnabled
-            available = isVisible
-            persistent = isPersistent
-            if (includeValue && isPersistent && this@toProto is TwoStatePreference) {
-                value = preferenceValueProto { booleanValue = this@toProto.isChecked }
-            }
-            this@toProto.fragment.toActionTarget(activityClass, preferenceExtras)?.let {
-                actionTarget = it
-                return@preferenceProto
-            }
-            this@toProto.intent?.let { actionTarget = it.toActionTarget() }
+    private suspend fun PreferenceScreen.toProto(intent: Intent?): PreferenceScreenProto =
+        preferenceScreenProto {
+            intent?.let { this.intent = it.toProto() }
+            root = (this@toProto as PreferenceGroup).toProto()
         }
 
-    private suspend fun PreferenceHierarchy.toProto(
-        activityClass: Class<*>,
-        isRoot: Boolean,
-    ): PreferenceGroupProto = preferenceGroupProto {
-        preference = toProto(this@toProto, activityClass, isRoot)
-        forEachAsync {
+    private suspend fun PreferenceGroup.toProto(): PreferenceGroupProto = preferenceGroupProto {
+        preference = (this@toProto as Preference).toProto()
+        for (index in 0 until preferenceCount) {
+            val child = getPreference(index)
             addPreferences(
                 preferenceOrGroupProto {
-                    if (it is PreferenceHierarchy) {
-                        group = it.toProto(activityClass, false)
+                    if (child is PreferenceGroup) {
+                        group = child.toProto()
                     } else {
-                        preference = toProto(it, activityClass, false)
+                        preference = child.toProto()
                     }
-                })
+                }
+            )
         }
     }
 
-    private suspend fun toProto(
-        node: PreferenceHierarchyNode,
-        activityClass: Class<*>,
-        isRoot: Boolean,
-    ) = preferenceProto {
+    private suspend fun Preference.toProto(): PreferenceProto = preferenceProto {
+        this@toProto.key?.let { key = it }
+        this@toProto.title?.let { title = textProto { string = it.toString() } }
+        this@toProto.summary?.let { summary = textProto { string = it.toString() } }
+        val preferenceExtras = peekExtras()
+        preferenceExtras?.let { extras = it.toProto() }
+        enabled = isEnabled
+        available = isVisible
+        persistent = isPersistent
+        if (includeValue && isPersistent && this@toProto is TwoStatePreference) {
+            value = preferenceValueProto { booleanValue = this@toProto.isChecked }
+        }
+        this@toProto.fragment.toActionTarget(preferenceExtras)?.let {
+            actionTarget = it
+            return@preferenceProto
+        }
+        this@toProto.intent?.let { actionTarget = it.toActionTarget() }
+    }
+
+    private suspend fun PreferenceHierarchy.toProto(isRoot: Boolean): PreferenceGroupProto =
+        preferenceGroupProto {
+            preference = toProto(this@toProto, isRoot)
+            forEachAsync {
+                addPreferences(
+                    preferenceOrGroupProto {
+                        if (it is PreferenceHierarchy) {
+                            group = it.toProto(false)
+                        } else {
+                            preference = toProto(it, false)
+                        }
+                    }
+                )
+            }
+        }
+
+    private suspend fun toProto(node: PreferenceHierarchyNode, isRoot: Boolean) = preferenceProto {
         val metadata = node.metadata
         key = metadata.key
         metadata.getTitleTextProto(isRoot)?.let { title = it }
@@ -318,22 +277,18 @@
             restricted = metadata.isRestricted(context)
         }
         persistent = metadata.isPersistent(context)
-        if (includeValue &&
-            persistent &&
-            metadata is BooleanValue &&
-            metadata is PersistentPreference<*>) {
+        if (
+            includeValue &&
+                persistent &&
+                metadata is BooleanValue &&
+                metadata is PersistentPreference<*>
+        ) {
             metadata.storage(context).getValue(metadata.key, Boolean::class.javaObjectType)?.let {
                 value = preferenceValueProto { booleanValue = it }
             }
         }
         if (metadata is PreferenceScreenMetadata) {
-            if (metadata.hasCompleteHierarchy()) {
-                @Suppress("CheckReturnValue") addPreferenceScreenMetadata(metadata, activityClass)
-            } else {
-                metadata.fragmentClass()?.toActionTarget(activityClass, preferenceExtras)?.let {
-                    actionTarget = it
-                }
-            }
+            @Suppress("CheckReturnValue") addPreferenceScreenMetadata(metadata)
         }
         metadata.intent(context)?.let { actionTarget = it.toActionTarget() }
     }
@@ -359,16 +314,13 @@
         }
     }
 
-    private suspend fun String?.toActionTarget(
-        activityClass: Class<*>,
-        extras: Bundle?,
-    ): ActionTarget? {
+    private suspend fun String?.toActionTarget(extras: Bundle?): ActionTarget? {
         if (this.isNullOrEmpty()) return null
         try {
             val fragmentClass = context.classLoader.loadClass(this)
             if (Fragment::class.java.isAssignableFrom(fragmentClass)) {
                 @Suppress("UNCHECKED_CAST")
-                return (fragmentClass as Class<out Fragment>).toActionTarget(activityClass, extras)
+                return (fragmentClass as Class<out Fragment>).toActionTarget(extras)
             }
         } catch (e: Exception) {
             Log.e(TAG, "Cannot loadClass $this", e)
@@ -376,16 +328,12 @@
         return null
     }
 
-    private suspend fun Class<out Fragment>.toActionTarget(
-        activityClass: Class<*>,
-        extras: Bundle?,
-    ): ActionTarget {
-        val startIntent = Intent(context, activityClass)
-        startIntent.putExtra(PreferenceActivity.EXTRA_SHOW_FRAGMENT, name)
-        extras?.let { startIntent.putExtra(PreferenceActivity.EXTRA_SHOW_FRAGMENT_ARGUMENTS, it) }
-        if (!PreferenceScreenProvider::class.java.isAssignableFrom(this) &&
-            !PreferenceScreenBindingKeyProvider::class.java.isAssignableFrom(this)) {
-            return actionTargetProto { intent = startIntent.toProto() }
+    private suspend fun Class<out Fragment>.toActionTarget(extras: Bundle?): ActionTarget? {
+        if (
+            !PreferenceScreenProvider::class.java.isAssignableFrom(this) &&
+                !PreferenceScreenBindingKeyProvider::class.java.isAssignableFrom(this)
+        ) {
+            return null
         }
         val fragment =
             withContext(Dispatchers.Main) {
@@ -398,18 +346,24 @@
             }
         if (fragment is PreferenceScreenBindingKeyProvider) {
             val screenKey = fragment.getPreferenceScreenBindingKey(context)
-            if (screenKey != null && addPreferenceScreenFromRegistry(screenKey, activityClass)) {
+            if (screenKey != null && addPreferenceScreenFromRegistry(screenKey)) {
                 return actionTargetProto { key = screenKey }
             }
         }
         if (fragment is PreferenceScreenProvider) {
-            val screen = fragment.createPreferenceScreen(preferenceScreenFactory)
-            if (screen != null) {
-                addPreferenceScreen(startIntent, activityClass, screen)
-                return actionTargetProto { key = screen.key }
+            try {
+                val screen = fragment.createPreferenceScreen(preferenceScreenFactory)
+                val screenKey = screen?.key
+                if (!screenKey.isNullOrEmpty()) {
+                    @Suppress("CheckReturnValue")
+                    addPreferenceScreen(screenKey) { screen.toProto(null) }
+                    return actionTargetProto { key = screenKey }
+                }
+            } catch (e: Exception) {
+                Log.e(TAG, "Fail to createPreferenceScreen for $fragment", e)
             }
         }
-        return actionTargetProto { intent = startIntent.toProto() }
+        return null
     }
 
     private suspend fun Intent.toActionTarget(): ActionTarget {
diff --git a/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceSetterApi.kt b/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceSetterApi.kt
new file mode 100644
index 0000000..d8db1bb
--- /dev/null
+++ b/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceSetterApi.kt
@@ -0,0 +1,170 @@
+/*
+ * 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.graph
+
+import android.app.Application
+import android.os.Bundle
+import androidx.annotation.IntDef
+import com.android.settingslib.graph.proto.PreferenceValueProto
+import com.android.settingslib.ipc.ApiDescriptor
+import com.android.settingslib.ipc.ApiHandler
+import com.android.settingslib.ipc.IntMessageCodec
+import com.android.settingslib.ipc.MessageCodec
+import com.android.settingslib.metadata.BooleanValue
+import com.android.settingslib.metadata.PersistentPreference
+import com.android.settingslib.metadata.PreferenceAvailabilityProvider
+import com.android.settingslib.metadata.PreferenceRestrictionProvider
+import com.android.settingslib.metadata.PreferenceScreenRegistry
+import com.android.settingslib.metadata.ReadWritePermit
+
+/** Request to set preference value. */
+data class PreferenceSetterRequest(
+    val screenKey: String,
+    val key: String,
+    val value: PreferenceValueProto,
+)
+
+/** Result of preference setter request. */
+@IntDef(
+    PreferenceSetterResult.OK,
+    PreferenceSetterResult.UNSUPPORTED,
+    PreferenceSetterResult.DISABLED,
+    PreferenceSetterResult.UNAVAILABLE,
+    PreferenceSetterResult.INVALID_REQUEST,
+    PreferenceSetterResult.INTERNAL_ERROR,
+)
+@Retention(AnnotationRetention.SOURCE)
+annotation class PreferenceSetterResult {
+    companion object {
+        /** Set preference value successfully. */
+        const val OK = 0
+        /** Set preference value is unsupported on the preference. */
+        const val UNSUPPORTED = 1
+        /** Preference is disabled and cannot set preference value. */
+        const val DISABLED = 2
+        /** Preference is restricted by managed configuration and cannot set preference value. */
+        const val RESTRICTED = 3
+        /** Preference is unavailable and cannot set preference value. */
+        const val UNAVAILABLE = 4
+        /** Require (runtime/special) app permission from user explicitly. */
+        const val REQUIRE_APP_PERMISSION = 5
+        /** Require explicit user agreement (e.g. terms of service). */
+        const val REQUIRE_USER_AGREEMENT = 6
+        /** Disallow to set preference value (e.g. uid not allowed). */
+        const val DISALLOW = 7
+        /** Request is invalid. */
+        const val INVALID_REQUEST = 8
+        /** Internal error happened when persist preference value. */
+        const val INTERNAL_ERROR = 9
+    }
+}
+
+/** Preference setter API descriptor. */
+class PreferenceSetterApiDescriptor(override val id: Int) :
+    ApiDescriptor<PreferenceSetterRequest, Int> {
+
+    override val requestCodec: MessageCodec<PreferenceSetterRequest>
+        get() = PreferenceSetterRequestCodec
+
+    override val responseCodec: MessageCodec<Int>
+        get() = IntMessageCodec
+}
+
+/** Preference setter API implementation. */
+class PreferenceSetterApiHandler(override val id: Int) : ApiHandler<PreferenceSetterRequest, Int> {
+
+    override fun hasPermission(
+        application: Application,
+        myUid: Int,
+        callingUid: Int,
+        request: PreferenceSetterRequest,
+    ): Boolean = true
+
+    override suspend fun invoke(
+        application: Application,
+        myUid: Int,
+        callingUid: Int,
+        request: PreferenceSetterRequest,
+    ): Int {
+        val screenMetadata =
+            PreferenceScreenRegistry[request.screenKey] ?: return PreferenceSetterResult.UNSUPPORTED
+        val key = request.key
+        val metadata =
+            screenMetadata.getPreferenceHierarchy(application).find(key)
+                ?: return PreferenceSetterResult.UNSUPPORTED
+        if (metadata !is PersistentPreference<*>) return PreferenceSetterResult.UNSUPPORTED
+        if (!metadata.isEnabled(application)) return PreferenceSetterResult.DISABLED
+        if (metadata is PreferenceRestrictionProvider && metadata.isRestricted(application)) {
+            return PreferenceSetterResult.RESTRICTED
+        }
+        if (metadata is PreferenceAvailabilityProvider && !metadata.isAvailable(application)) {
+            return PreferenceSetterResult.UNAVAILABLE
+        }
+        val storage = metadata.storage(application)
+        val value = request.value
+        try {
+            if (value.hasBooleanValue()) {
+                if (metadata !is BooleanValue) return PreferenceSetterResult.INVALID_REQUEST
+                val booleanValue = value.booleanValue
+                @Suppress("UNCHECKED_CAST")
+                val booleanPreference = metadata as PersistentPreference<Boolean>
+                when (
+                    booleanPreference.getWritePermit(application, booleanValue, myUid, callingUid)
+                ) {
+                    ReadWritePermit.ALLOW -> {}
+                    ReadWritePermit.DISALLOW -> return PreferenceSetterResult.DISALLOW
+                    ReadWritePermit.REQUIRE_APP_PERMISSION ->
+                        return PreferenceSetterResult.REQUIRE_APP_PERMISSION
+                    ReadWritePermit.REQUIRE_USER_AGREEMENT ->
+                        return PreferenceSetterResult.REQUIRE_USER_AGREEMENT
+                    else -> return PreferenceSetterResult.INTERNAL_ERROR
+                }
+                storage.setValue(key, Boolean::class.javaObjectType, booleanValue)
+                return PreferenceSetterResult.OK
+            }
+        } catch (e: Exception) {
+            return PreferenceSetterResult.INTERNAL_ERROR
+        }
+        return PreferenceSetterResult.INVALID_REQUEST
+    }
+
+    override val requestCodec: MessageCodec<PreferenceSetterRequest>
+        get() = PreferenceSetterRequestCodec
+
+    override val responseCodec: MessageCodec<Int>
+        get() = IntMessageCodec
+}
+
+/** Message codec for [PreferenceSetterRequest]. */
+object PreferenceSetterRequestCodec : MessageCodec<PreferenceSetterRequest> {
+    override fun encode(data: PreferenceSetterRequest) =
+        Bundle(3).apply {
+            putString(SCREEN_KEY, data.screenKey)
+            putString(KEY, data.key)
+            putByteArray(null, data.value.toByteArray())
+        }
+
+    override fun decode(data: Bundle) =
+        PreferenceSetterRequest(
+            data.getString(SCREEN_KEY)!!,
+            data.getString(KEY)!!,
+            PreferenceValueProto.parseFrom(data.getByteArray(null)!!),
+        )
+
+    private const val SCREEN_KEY = "s"
+    private const val KEY = "k"
+}
diff --git a/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceHierarchy.kt b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceHierarchy.kt
index 6992005..94d373b 100644
--- a/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceHierarchy.kt
+++ b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceHierarchy.kt
@@ -17,7 +17,15 @@
 package com.android.settingslib.metadata
 
 /** A node in preference hierarchy that is associated with [PreferenceMetadata]. */
-open class PreferenceHierarchyNode internal constructor(val metadata: PreferenceMetadata)
+open class PreferenceHierarchyNode internal constructor(val metadata: PreferenceMetadata) {
+    /**
+     * Preference order in the hierarchy.
+     *
+     * When a preference appears on different screens, different order values could be specified.
+     */
+    var order: Int? = null
+        internal set
+}
 
 /**
  * Preference hierarchy describes the structure of preferences recursively.
@@ -30,9 +38,7 @@
     private val children = mutableListOf<PreferenceHierarchyNode>()
 
     /** Adds a preference to the hierarchy. */
-    operator fun PreferenceMetadata.unaryPlus() {
-        children.add(PreferenceHierarchyNode(this))
-    }
+    operator fun PreferenceMetadata.unaryPlus() = +PreferenceHierarchyNode(this)
 
     /**
      * Adds preference screen with given key (as a placeholder) to the hierarchy.
@@ -45,35 +51,67 @@
      *
      * @throws NullPointerException if screen is not registered to [PreferenceScreenRegistry]
      */
-    operator fun String.unaryPlus() {
-        children.add(PreferenceHierarchyNode(PreferenceScreenRegistry[this]!!))
-    }
+    operator fun String.unaryPlus() = +PreferenceHierarchyNode(PreferenceScreenRegistry[this]!!)
+
+    operator fun PreferenceHierarchyNode.unaryPlus() = also { children.add(it) }
+
+    /** Specifies preference order in the hierarchy. */
+    infix fun PreferenceHierarchyNode.order(order: Int) = apply { this.order = order }
 
     /** Adds a preference to the hierarchy. */
-    fun add(metadata: PreferenceMetadata) {
-        children.add(PreferenceHierarchyNode(metadata))
+    @JvmOverloads
+    fun add(metadata: PreferenceMetadata, order: Int? = null) {
+        PreferenceHierarchyNode(metadata).also {
+            it.order = order
+            children.add(it)
+        }
     }
 
-    /** Adds a preference to the hierarchy before a specific preference. */
+    /** Adds a preference to the hierarchy before given key. */
     fun addBefore(key: String, metadata: PreferenceMetadata) {
-        var foundIndex = children.indexOfFirst { it.metadata.key == key }
-        if (foundIndex == -1) foundIndex = children.size
-        children.add(foundIndex, PreferenceHierarchyNode(metadata))
+        val (list, index) = findPreference(key) ?: (children to children.size)
+        list.add(index, PreferenceHierarchyNode(metadata))
     }
 
-    /** Adds a preference to the hierarchy after a specific preference. */
+    /** Adds a preference group to the hierarchy before given key. */
+    fun addGroupBefore(key: String, metadata: PreferenceMetadata): PreferenceHierarchy {
+        val (list, index) = findPreference(key) ?: (children to children.size)
+        return PreferenceHierarchy(metadata).also { list.add(index, it) }
+    }
+
+    /** Adds a preference to the hierarchy after given key. */
     fun addAfter(key: String, metadata: PreferenceMetadata) {
-        var foundIndex = children.indexOfFirst { it.metadata.key == key }
-        if (foundIndex == -1) foundIndex = children.size else foundIndex++
-        children.add(foundIndex, PreferenceHierarchyNode(metadata))
+        val (list, index) = findPreference(key) ?: (children to children.size - 1)
+        list.add(index + 1, PreferenceHierarchyNode(metadata))
+    }
+
+    /** Adds a preference group to the hierarchy after given key. */
+    fun addGroupAfter(key: String, metadata: PreferenceMetadata): PreferenceHierarchy {
+        val (list, index) = findPreference(key) ?: (children to children.size - 1)
+        return PreferenceHierarchy(metadata).also { list.add(index + 1, it) }
+    }
+
+    private fun findPreference(key: String): Pair<MutableList<PreferenceHierarchyNode>, Int>? {
+        children.forEachIndexed { index, node ->
+            if (node.metadata.key == key) return children to index
+            if (node is PreferenceHierarchy) {
+                val result = node.findPreference(key)
+                if (result != null) return result
+            }
+        }
+        return null
     }
 
     /** Adds a preference group to the hierarchy. */
     operator fun PreferenceGroup.unaryPlus() = PreferenceHierarchy(this).also { children.add(it) }
 
     /** Adds a preference group and returns its preference hierarchy. */
-    fun addGroup(metadata: PreferenceGroup): PreferenceHierarchy =
-        PreferenceHierarchy(metadata).also { children.add(it) }
+    @JvmOverloads
+    fun addGroup(metadata: PreferenceGroup, order: Int? = null): PreferenceHierarchy =
+        PreferenceHierarchy(metadata).also {
+            this.order = order
+            children.add(it)
+        }
 
     /**
      * Adds preference screen with given key (as a placeholder) to the hierarchy.
@@ -91,7 +129,7 @@
     }
 
     /** Extensions to add more preferences to the hierarchy. */
-    operator fun plusAssign(init: PreferenceHierarchy.() -> Unit) = init(this)
+    operator fun PreferenceHierarchy.plusAssign(init: PreferenceHierarchy.() -> Unit) = init(this)
 
     /** Traversals preference hierarchy and applies given action. */
     fun forEach(action: (PreferenceHierarchyNode) -> Unit) {
@@ -117,17 +155,17 @@
         return null
     }
 
-    /** Returns all the [PreferenceMetadata]s appear in the hierarchy. */
-    fun getAllPreferences(): List<PreferenceMetadata> =
-        mutableListOf<PreferenceMetadata>().also { getAllPreferences(it) }
+    /** Returns all the [PreferenceHierarchyNode]s appear in the hierarchy. */
+    fun getAllPreferences(): List<PreferenceHierarchyNode> =
+        mutableListOf<PreferenceHierarchyNode>().also { getAllPreferences(it) }
 
-    private fun getAllPreferences(result: MutableList<PreferenceMetadata>) {
-        result.add(metadata)
+    private fun getAllPreferences(result: MutableList<PreferenceHierarchyNode>) {
+        result.add(this)
         for (child in children) {
             if (child is PreferenceHierarchy) {
                 child.getAllPreferences(result)
             } else {
-                result.add(child.metadata)
+                result.add(child)
             }
         }
     }
diff --git a/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceMetadata.kt b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceMetadata.kt
index 33f2dbf..10087a7 100644
--- a/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceMetadata.kt
+++ b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceMetadata.kt
@@ -19,10 +19,10 @@
 import android.content.Context
 import android.content.Intent
 import android.os.Bundle
-import androidx.fragment.app.Fragment
 import androidx.annotation.AnyThread
 import androidx.annotation.DrawableRes
 import androidx.annotation.StringRes
+import androidx.fragment.app.Fragment
 
 /**
  * Interface provides preference metadata (title, summary, icon, etc.).
@@ -134,9 +134,6 @@
     /** Returns preference intent. */
     fun intent(context: Context): Intent? = null
 
-    /** Returns preference order. */
-    fun order(context: Context): Int? = null
-
     /**
      * Returns the preference title.
      *
diff --git a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceBinding.kt b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceBinding.kt
index bcfef67..72cf403 100644
--- a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceBinding.kt
+++ b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceBinding.kt
@@ -80,7 +80,6 @@
             preference.isVisible =
                 (this as? PreferenceAvailabilityProvider)?.isAvailable(context) != false
             preference.isPersistent = isPersistent(context)
-            metadata.order(context)?.let { preference.order = it }
             // PreferenceRegistry will notify dependency change, so we do not need to set
             // dependency here. This simplifies dependency management and avoid the
             // IllegalStateException when call Preference.setDependency
diff --git a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceBindingFactory.kt b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceBindingFactory.kt
index 43f2cb6..87c289f 100644
--- a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceBindingFactory.kt
+++ b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceBindingFactory.kt
@@ -16,14 +16,33 @@
 
 package com.android.settingslib.preference
 
+import androidx.preference.Preference
 import com.android.settingslib.metadata.MainSwitchPreference
 import com.android.settingslib.metadata.PreferenceGroup
+import com.android.settingslib.metadata.PreferenceHierarchyNode
 import com.android.settingslib.metadata.PreferenceMetadata
 import com.android.settingslib.metadata.SwitchPreference
 
 /** Factory to map [PreferenceMetadata] to [PreferenceBinding]. */
 interface PreferenceBindingFactory {
 
+    /**
+     * Binds [Preference] and its associated [PreferenceMetadata] with given [PreferenceBinding]
+     * (`getPreferenceBinding(metadata)` is used if [preferenceBinding] is `null`).
+     *
+     * Subclass could override this callback to handle common binding logic. For instance,
+     * restricted preference with policy transparency.
+     */
+    fun bind(
+        preference: Preference,
+        node: PreferenceHierarchyNode,
+        preferenceBinding: PreferenceBinding? = null,
+    ) {
+        val binding = (preferenceBinding ?: getPreferenceBinding(node.metadata)) ?: return
+        binding.bind(preference, node.metadata)
+        node.order?.let { preference.order = it }
+    }
+
     /** Returns the [PreferenceBinding] associated with the [PreferenceMetadata]. */
     fun getPreferenceBinding(metadata: PreferenceMetadata): PreferenceBinding?
 }
diff --git a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceFragment.kt b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceFragment.kt
index 21621a8..5f4b88f 100644
--- a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceFragment.kt
+++ b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceFragment.kt
@@ -129,7 +129,7 @@
     }
 
     protected fun getPreferenceKeysInHierarchy(): Set<String> =
-        preferenceScreenBindingHelper?.getPreferences()?.map { it.key }?.toSet() ?: setOf()
+        preferenceScreenBindingHelper?.getPreferences()?.map { it.metadata.key }?.toSet() ?: setOf()
 
     companion object {
         private const val TAG = "PreferenceFragment"
diff --git a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceHierarchyInflater.kt b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceHierarchyInflater.kt
index 5ef7823..c26ba18 100644
--- a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceHierarchyInflater.kt
+++ b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceHierarchyInflater.kt
@@ -21,7 +21,6 @@
 import com.android.settingslib.datastore.KeyValueStore
 import com.android.settingslib.metadata.PersistentPreference
 import com.android.settingslib.metadata.PreferenceHierarchy
-import com.android.settingslib.metadata.PreferenceMetadata
 
 /** Inflates [PreferenceHierarchy] into given [PreferenceGroup] recursively. */
 fun PreferenceGroup.inflatePreferenceHierarchy(
@@ -29,12 +28,11 @@
     hierarchy: PreferenceHierarchy,
     storages: MutableMap<KeyValueStore, PreferenceDataStore> = mutableMapOf(),
 ) {
-    fun PreferenceMetadata.preferenceBinding() = preferenceBindingFactory.getPreferenceBinding(this)
-
-    hierarchy.metadata.let { it.preferenceBinding()?.bind(this, it) }
+    preferenceBindingFactory.bind(this, hierarchy)
     hierarchy.forEach {
         val metadata = it.metadata
-        val preferenceBinding = metadata.preferenceBinding() ?: return@forEach
+        val preferenceBinding =
+            preferenceBindingFactory.getPreferenceBinding(metadata) ?: return@forEach
         val widget = preferenceBinding.createWidget(context)
         if (it is PreferenceHierarchy) {
             val preferenceGroup = widget as PreferenceGroup
@@ -42,7 +40,7 @@
             addPreference(preferenceGroup)
             preferenceGroup.inflatePreferenceHierarchy(preferenceBindingFactory, it)
         } else {
-            preferenceBinding.bind(widget, metadata)
+            preferenceBindingFactory.bind(widget, it, preferenceBinding)
             (metadata as? PersistentPreference<*>)?.storage(context)?.let { storage ->
                 widget.preferenceDataStore =
                     storages.getOrPut(storage) { PreferenceDataStoreAdapter(storage) }
diff --git a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceScreenBindingHelper.kt b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceScreenBindingHelper.kt
index d99d470..7af9c81 100644
--- a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceScreenBindingHelper.kt
+++ b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceScreenBindingHelper.kt
@@ -19,25 +19,24 @@
 import android.content.Context
 import android.content.Intent
 import android.os.Bundle
-import android.os.Handler
-import android.os.Looper
 import androidx.preference.Preference
 import androidx.preference.PreferenceDataStore
 import androidx.preference.PreferenceGroup
 import androidx.preference.PreferenceScreen
+import com.android.settingslib.datastore.HandlerExecutor
 import com.android.settingslib.datastore.KeyValueStore
 import com.android.settingslib.datastore.KeyedDataObservable
 import com.android.settingslib.datastore.KeyedObservable
 import com.android.settingslib.datastore.KeyedObserver
 import com.android.settingslib.metadata.PersistentPreference
 import com.android.settingslib.metadata.PreferenceHierarchy
+import com.android.settingslib.metadata.PreferenceHierarchyNode
 import com.android.settingslib.metadata.PreferenceLifecycleContext
 import com.android.settingslib.metadata.PreferenceLifecycleProvider
 import com.android.settingslib.metadata.PreferenceMetadata
 import com.android.settingslib.metadata.PreferenceScreenRegistry
 import com.google.common.collect.ImmutableMap
 import com.google.common.collect.ImmutableMultimap
-import java.util.concurrent.Executor
 
 /**
  * Helper to bind preferences on given [preferenceScreen].
@@ -54,13 +53,7 @@
     private val preferenceHierarchy: PreferenceHierarchy,
 ) : KeyedDataObservable<String>() {
 
-    private val handler = Handler(Looper.getMainLooper())
-    private val executor =
-        object : Executor {
-            override fun execute(command: Runnable) {
-                handler.post(command)
-            }
-        }
+    private val mainExecutor = HandlerExecutor.main
 
     private val preferenceLifecycleContext =
         object : PreferenceLifecycleContext(context) {
@@ -75,7 +68,7 @@
             ) = fragment.startActivityForResult(intent, requestCode, options)
         }
 
-    private val preferences: ImmutableMap<String, PreferenceMetadata>
+    private val preferences: ImmutableMap<String, PreferenceHierarchyNode>
     private val dependencies: ImmutableMultimap<String, String>
     private val lifecycleAwarePreferences: Array<PreferenceLifecycleProvider>
     private val storages = mutableSetOf<KeyedObservable<String>>()
@@ -90,27 +83,29 @@
         }
 
     init {
-        val preferencesBuilder = ImmutableMap.builder<String, PreferenceMetadata>()
+        val preferencesBuilder = ImmutableMap.builder<String, PreferenceHierarchyNode>()
         val dependenciesBuilder = ImmutableMultimap.builder<String, String>()
         val lifecycleAwarePreferences = mutableListOf<PreferenceLifecycleProvider>()
         fun PreferenceMetadata.addDependency(dependency: PreferenceMetadata) {
             dependenciesBuilder.put(key, dependency.key)
         }
 
-        fun PreferenceMetadata.add() {
-            preferencesBuilder.put(key, this)
-            dependencyOfEnabledState(context)?.addDependency(this)
-            if (this is PreferenceLifecycleProvider) lifecycleAwarePreferences.add(this)
-            if (this is PersistentPreference<*>) storages.add(storage(context))
+        fun PreferenceHierarchyNode.addNode() {
+            metadata.let {
+                preferencesBuilder.put(it.key, this)
+                it.dependencyOfEnabledState(context)?.addDependency(it)
+                if (it is PreferenceLifecycleProvider) lifecycleAwarePreferences.add(it)
+                if (it is PersistentPreference<*>) storages.add(it.storage(context))
+            }
         }
 
         fun PreferenceHierarchy.addPreferences() {
-            metadata.add()
+            addNode()
             forEach {
                 if (it is PreferenceHierarchy) {
                     it.addPreferences()
                 } else {
-                    it.metadata.add()
+                    it.addNode()
                 }
             }
         }
@@ -121,8 +116,8 @@
         this.lifecycleAwarePreferences = lifecycleAwarePreferences.toTypedArray()
 
         preferenceObserver = KeyedObserver { key, reason -> onPreferenceChange(key, reason) }
-        addObserver(preferenceObserver, executor)
-        for (storage in storages) storage.addObserver(storageObserver, executor)
+        addObserver(preferenceObserver, mainExecutor)
+        for (storage in storages) storage.addObserver(storageObserver, mainExecutor)
     }
 
     private fun onPreferenceChange(key: String?, reason: Int) {
@@ -130,7 +125,7 @@
 
         // bind preference to update UI
         preferenceScreen.findPreference<Preference>(key)?.let {
-            preferenceBindingFactory.bind(it, preferences[key])
+            preferences[key]?.let { node -> preferenceBindingFactory.bind(it, node) }
         }
 
         // check reason to avoid potential infinite loop
@@ -225,15 +220,15 @@
         ) =
             preferenceScreen.bindRecursively(
                 preferenceBindingFactory,
-                preferenceHierarchy.getAllPreferences().associateBy { it.key },
+                preferenceHierarchy.getAllPreferences().associateBy { it.metadata.key },
             )
 
         private fun PreferenceGroup.bindRecursively(
             preferenceBindingFactory: PreferenceBindingFactory,
-            preferences: Map<String, PreferenceMetadata>,
+            preferences: Map<String, PreferenceHierarchyNode>,
             storages: MutableMap<KeyValueStore, PreferenceDataStore> = mutableMapOf(),
         ) {
-            preferenceBindingFactory.bind(this, preferences[key])
+            preferences[key]?.let { preferenceBindingFactory.bind(this, it) }
             val count = preferenceCount
             for (index in 0 until count) {
                 val preference = getPreference(index)
@@ -241,7 +236,7 @@
                     preference.bindRecursively(preferenceBindingFactory, preferences, storages)
                 } else {
                     preferences[preference.key]?.let {
-                        preferenceBindingFactory.getPreferenceBinding(it)?.bind(preference, it)
+                        preferenceBindingFactory.bind(preference, it)
                         (it as? PersistentPreference<*>)?.storage(context)?.let { storage ->
                             preference.preferenceDataStore =
                                 storages.getOrPut(storage) { PreferenceDataStoreAdapter(storage) }
@@ -250,10 +245,5 @@
                 }
             }
         }
-
-        private fun PreferenceBindingFactory.bind(
-            preference: Preference,
-            metadata: PreferenceMetadata?,
-        ) = metadata?.let { getPreferenceBinding(it)?.bind(preference, it) }
     }
 }
diff --git a/packages/SettingsLib/Service/src/com/android/settingslib/service/PreferenceGraphApi.kt b/packages/SettingsLib/Service/src/com/android/settingslib/service/PreferenceGraphApi.kt
index 95661c9..6e38df1 100644
--- a/packages/SettingsLib/Service/src/com/android/settingslib/service/PreferenceGraphApi.kt
+++ b/packages/SettingsLib/Service/src/com/android/settingslib/service/PreferenceGraphApi.kt
@@ -21,8 +21,8 @@
 import com.android.settingslib.graph.GetPreferenceGraphRequest
 
 /** Api to get preference graph. */
-internal class PreferenceGraphApi(activityClasses: Set<String>) :
-    GetPreferenceGraphApiHandler(activityClasses) {
+internal class PreferenceGraphApi : GetPreferenceGraphApiHandler() {
+
     override val id: Int
         get() = API_GET_PREFERENCE_GRAPH
 
@@ -31,7 +31,5 @@
         myUid: Int,
         callingUid: Int,
         request: GetPreferenceGraphRequest,
-    ): Boolean {
-        return true // TODO: add permission check
-    }
+    ) = true
 }
diff --git a/packages/SettingsLib/Service/src/com/android/settingslib/service/PreferenceService.kt b/packages/SettingsLib/Service/src/com/android/settingslib/service/PreferenceService.kt
index d382dad..8ebb145 100644
--- a/packages/SettingsLib/Service/src/com/android/settingslib/service/PreferenceService.kt
+++ b/packages/SettingsLib/Service/src/com/android/settingslib/service/PreferenceService.kt
@@ -16,6 +16,7 @@
 
 package com.android.settingslib.service
 
+import com.android.settingslib.graph.PreferenceSetterApiHandler
 import com.android.settingslib.ipc.ApiHandler
 import com.android.settingslib.ipc.MessengerService
 import com.android.settingslib.ipc.PermissionChecker
@@ -31,7 +32,10 @@
     name: String = "PreferenceService",
 ) :
     MessengerService(
-        listOf<ApiHandler<*, *>>(PreferenceGraphApi(setOf())),
+        listOf<ApiHandler<*, *>>(
+            PreferenceGraphApi(),
+            PreferenceSetterApiHandler(API_PREFERENCE_SETTER),
+        ),
         permissionChecker,
         name,
     )
diff --git a/packages/SettingsLib/Service/src/com/android/settingslib/service/ServiceApiConstants.kt b/packages/SettingsLib/Service/src/com/android/settingslib/service/ServiceApiConstants.kt
index 8f03111..1f38a66 100644
--- a/packages/SettingsLib/Service/src/com/android/settingslib/service/ServiceApiConstants.kt
+++ b/packages/SettingsLib/Service/src/com/android/settingslib/service/ServiceApiConstants.kt
@@ -19,3 +19,4 @@
 const val PREFERENCE_SERVICE_ACTION = "com.android.settingslib.PREFERENCE_SERVICE"
 
 internal const val API_GET_PREFERENCE_GRAPH = 1
+internal const val API_PREFERENCE_SETTER = 2
diff --git a/packages/SettingsLib/aconfig/settingslib.aconfig b/packages/SettingsLib/aconfig/settingslib.aconfig
index 5a8763f..81a2e6a 100644
--- a/packages/SettingsLib/aconfig/settingslib.aconfig
+++ b/packages/SettingsLib/aconfig/settingslib.aconfig
@@ -159,3 +159,10 @@
         purpose: PURPOSE_BUGFIX
     }
 }
+
+flag {
+    name: "hearing_devices_ambient_volume_control"
+    namespace: "accessibility"
+    description: "Enable the ambient volume control in device details and hearing devices dialog."
+    bug: "357878944"
+}
diff --git a/packages/SettingsLib/res/values-as/strings.xml b/packages/SettingsLib/res/values-as/strings.xml
index 83cd2fe..62d6f68 100644
--- a/packages/SettingsLib/res/values-as/strings.xml
+++ b/packages/SettingsLib/res/values-as/strings.xml
@@ -357,10 +357,8 @@
     <string name="enhanced_connectivity_summary" msgid="1576414159820676330">"উন্নত সংযোগ সুবিধাটো সক্ষম কৰে।"</string>
     <string name="enable_terminal_title" msgid="3834790541986303654">"স্থানীয় টাৰ্মিনেল"</string>
     <string name="enable_terminal_summary" msgid="2481074834856064500">"স্থানীয় শ্বেলৰ এক্সেছ দিয়া টাৰ্মিনেল এপ্ সক্ষম কৰক"</string>
-    <!-- no translation found for enable_linux_terminal_title (5076044866895670637) -->
-    <skip />
-    <!-- no translation found for enable_linux_terminal_summary (5893216510985145320) -->
-    <skip />
+    <string name="enable_linux_terminal_title" msgid="5076044866895670637">"Linux বিকাশৰ পৰিৱেশ"</string>
+    <string name="enable_linux_terminal_summary" msgid="5893216510985145320">"Androidত Linux টাৰ্মিনেল চলাওক"</string>
     <string name="hdcp_checking_title" msgid="3155692785074095986">"HDCP পৰীক্ষণ"</string>
     <string name="hdcp_checking_dialog_title" msgid="7691060297616217781">"HDCP পৰীক্ষণ আচৰণ ছেট কৰক"</string>
     <string name="debug_debugging_category" msgid="535341063709248842">"ডিবাগিং"</string>
diff --git a/packages/SettingsLib/res/values-bn/strings.xml b/packages/SettingsLib/res/values-bn/strings.xml
index f71a003c..ca98e0e 100644
--- a/packages/SettingsLib/res/values-bn/strings.xml
+++ b/packages/SettingsLib/res/values-bn/strings.xml
@@ -357,10 +357,8 @@
     <string name="enhanced_connectivity_summary" msgid="1576414159820676330">"কানেক্টিভিটি ফিচার উন্নত করার বিষয়টি চালু করা হয়েছে।"</string>
     <string name="enable_terminal_title" msgid="3834790541986303654">"স্থানীয় টার্মিনাল"</string>
     <string name="enable_terminal_summary" msgid="2481074834856064500">"স্থানীয় শেল অ্যাক্সেসের প্রস্তাব করে এমন টার্মিনাল অ্যাপ্লিকেশন সক্ষম করুন"</string>
-    <!-- no translation found for enable_linux_terminal_title (5076044866895670637) -->
-    <skip />
-    <!-- no translation found for enable_linux_terminal_summary (5893216510985145320) -->
-    <skip />
+    <string name="enable_linux_terminal_title" msgid="5076044866895670637">"Linux ডেভেলপমেন্ট এনভায়র্নমেন্ট"</string>
+    <string name="enable_linux_terminal_summary" msgid="5893216510985145320">"Android-এ Linux টার্মিনাল রান করান"</string>
     <string name="hdcp_checking_title" msgid="3155692785074095986">"HDCP পরীক্ষণ"</string>
     <string name="hdcp_checking_dialog_title" msgid="7691060297616217781">"HDCP চেক করার আচরণ সেট করুন"</string>
     <string name="debug_debugging_category" msgid="535341063709248842">"ডিবাগিং"</string>
diff --git a/packages/SettingsLib/res/values-de/strings.xml b/packages/SettingsLib/res/values-de/strings.xml
index 1a5e15a..75ebb57 100644
--- a/packages/SettingsLib/res/values-de/strings.xml
+++ b/packages/SettingsLib/res/values-de/strings.xml
@@ -357,10 +357,8 @@
     <string name="enhanced_connectivity_summary" msgid="1576414159820676330">"Aktiviert die Funktion \"Verbesserte Konnektivität\"."</string>
     <string name="enable_terminal_title" msgid="3834790541986303654">"Lokales Terminal"</string>
     <string name="enable_terminal_summary" msgid="2481074834856064500">"Terminal-App mit Zugriff auf lokale Shell aktivieren"</string>
-    <!-- no translation found for enable_linux_terminal_title (5076044866895670637) -->
-    <skip />
-    <!-- no translation found for enable_linux_terminal_summary (5893216510985145320) -->
-    <skip />
+    <string name="enable_linux_terminal_title" msgid="5076044866895670637">"Linux-Entwicklungsumgebung"</string>
+    <string name="enable_linux_terminal_summary" msgid="5893216510985145320">"Linux-Terminal unter Android ausführen"</string>
     <string name="hdcp_checking_title" msgid="3155692785074095986">"HDCP-Prüfung"</string>
     <string name="hdcp_checking_dialog_title" msgid="7691060297616217781">"HDCP-Prüfverhalten festlegen"</string>
     <string name="debug_debugging_category" msgid="535341063709248842">"Debugging"</string>
diff --git a/packages/SettingsLib/res/values-eu/strings.xml b/packages/SettingsLib/res/values-eu/strings.xml
index 2c512b7..48e4f62e 100644
--- a/packages/SettingsLib/res/values-eu/strings.xml
+++ b/packages/SettingsLib/res/values-eu/strings.xml
@@ -565,7 +565,7 @@
     <string name="alarms_and_reminders_switch_title" msgid="4939393911531826222">"Eman alarmak eta abisuak ezartzeko baimena"</string>
     <string name="alarms_and_reminders_title" msgid="8819933264635406032">"Alarmak eta abisuak"</string>
     <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Eman alarmak ezartzeko eta denbora-muga duten ekintzak programatzeko baimena aplikazioari. Hala, aplikazioak atzeko planoan funtzionatuko du, eta litekeena da bateria gehiago kontsumitzea.\n\nBaimen hori ematen ez baduzu, ez dute funtzionatuko aplikazio honen bidez programatutako alarmek eta denbora-muga duten ekintzek."</string>
-    <string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"programazioa, alarma, abisua, erlojua"</string>
+    <string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"programazioa, alarma, gogorarazpena, erlojua"</string>
     <string name="zen_mode_do_not_disturb_name" msgid="6798711401734798283">"Ez molestatzeko"</string>
     <string name="zen_mode_settings_title" msgid="7374070457626419755">"Ez molestatzeko modua"</string>
     <string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Aktibatu"</string>
diff --git a/packages/SettingsLib/res/values-gu/strings.xml b/packages/SettingsLib/res/values-gu/strings.xml
index f343ff4..05ec0e1 100644
--- a/packages/SettingsLib/res/values-gu/strings.xml
+++ b/packages/SettingsLib/res/values-gu/strings.xml
@@ -357,10 +357,8 @@
     <string name="enhanced_connectivity_summary" msgid="1576414159820676330">"કનેક્ટિવિટીની વિસ્તૃત સુવિધા ચાલુ કરે છે."</string>
     <string name="enable_terminal_title" msgid="3834790541986303654">"સ્થાનિક ટર્મિનલ"</string>
     <string name="enable_terminal_summary" msgid="2481074834856064500">"સ્થાનિક શેલ અ‍ૅક્સેસની ઑફર કરતી ટર્મિનલ એપ્લિકેશનને સક્ષમ કરો"</string>
-    <!-- no translation found for enable_linux_terminal_title (5076044866895670637) -->
-    <skip />
-    <!-- no translation found for enable_linux_terminal_summary (5893216510985145320) -->
-    <skip />
+    <string name="enable_linux_terminal_title" msgid="5076044866895670637">"Linux ડેવલપમેન્ટ એન્વાયરમેન્ટ"</string>
+    <string name="enable_linux_terminal_summary" msgid="5893216510985145320">"Android પર Linux ટર્મિનલ ચલાવો"</string>
     <string name="hdcp_checking_title" msgid="3155692785074095986">"HDCP તપાસણી"</string>
     <string name="hdcp_checking_dialog_title" msgid="7691060297616217781">"HDCP તપાસણીની વર્તણૂક બદલો"</string>
     <string name="debug_debugging_category" msgid="535341063709248842">"ડીબગિંગ"</string>
@@ -522,7 +520,7 @@
     <string name="battery_info_status_charging_fast_v2" msgid="1825439848151256589">"ઝડપી ચાર્જિંગ"</string>
     <string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"વ્યવસ્થાપક દ્વારા નિયંત્રિત"</string>
     <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"પ્રતિબંધિત સેટિંગ દ્વારા નિયંત્રિત"</string>
-    <string name="disabled" msgid="8017887509554714950">"અક્ષમ કર્યો"</string>
+    <string name="disabled" msgid="8017887509554714950">"બંધ કરી"</string>
     <string name="external_source_trusted" msgid="1146522036773132905">"મંજૂરી છે"</string>
     <string name="external_source_untrusted" msgid="5037891688911672227">"મંજૂરી નથી"</string>
     <string name="install_other_apps" msgid="3232595082023199454">"અજાણી ઍપ ઇન્સ્ટૉલ કરો"</string>
diff --git a/packages/SettingsLib/res/values-hr/strings.xml b/packages/SettingsLib/res/values-hr/strings.xml
index 0df2a7a..2cf02fc 100644
--- a/packages/SettingsLib/res/values-hr/strings.xml
+++ b/packages/SettingsLib/res/values-hr/strings.xml
@@ -674,7 +674,7 @@
     <string name="guest_exit_save_data_button" msgid="3690974510644963547">"Spremi"</string>
     <string name="guest_exit_button" msgid="5774985819191803960">"Izađi iz načina rada za goste"</string>
     <string name="guest_reset_button" msgid="2515069346223503479">"Poništi gostujuću sesiju"</string>
-    <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"Izlaz iz gostujuće sesije"</string>
+    <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"Zatvori sesiju gosta"</string>
     <string name="guest_notification_ephemeral" msgid="7263252466950923871">"Sve će se aktivnosti izbrisati po izlasku"</string>
     <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"Svoje aktivnosti možete spremiti ili izbrisati na izlasku"</string>
     <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"Poništite da odmah izbrišete aktivnost sesije. Inače je možete spremiti ili izbrisati na izlasku."</string>
diff --git a/packages/SettingsLib/res/values-lo/strings.xml b/packages/SettingsLib/res/values-lo/strings.xml
index da631b6..740586e 100644
--- a/packages/SettingsLib/res/values-lo/strings.xml
+++ b/packages/SettingsLib/res/values-lo/strings.xml
@@ -357,10 +357,8 @@
     <string name="enhanced_connectivity_summary" msgid="1576414159820676330">"ເປີດນຳໃຊ້ຄຸນສົມບັດການເຊື່ອມຕໍ່ທີ່ເສີມແຕ່ງແລ້ວ"</string>
     <string name="enable_terminal_title" msgid="3834790541986303654">"Terminal ໃນໂຕເຄື່ອງ"</string>
     <string name="enable_terminal_summary" msgid="2481074834856064500">"ເປີດນຳໃຊ້ແອັບຯ Terminal ທີ່ໃຫ້ການເຂົ້າເຖິງ shell ໃນໂຕເຄື່ອງໄດ້"</string>
-    <!-- no translation found for enable_linux_terminal_title (5076044866895670637) -->
-    <skip />
-    <!-- no translation found for enable_linux_terminal_summary (5893216510985145320) -->
-    <skip />
+    <string name="enable_linux_terminal_title" msgid="5076044866895670637">"ສະພາບແວດລ້ອມໃນການພັດທະນາ Linux"</string>
+    <string name="enable_linux_terminal_summary" msgid="5893216510985145320">"ເອີ້ນໃຊ້ເທີມິນອນ Linux ຢູ່ Android"</string>
     <string name="hdcp_checking_title" msgid="3155692785074095986">"ການກວດສອບ HDCP"</string>
     <string name="hdcp_checking_dialog_title" msgid="7691060297616217781">"ຕັ້ງວິທີການກວດສອບ HDCP"</string>
     <string name="debug_debugging_category" msgid="535341063709248842">"ການດີບັກ"</string>
diff --git a/packages/SettingsLib/res/values-mn/strings.xml b/packages/SettingsLib/res/values-mn/strings.xml
index 615edff..4f247ab 100644
--- a/packages/SettingsLib/res/values-mn/strings.xml
+++ b/packages/SettingsLib/res/values-mn/strings.xml
@@ -357,10 +357,8 @@
     <string name="enhanced_connectivity_summary" msgid="1576414159820676330">"Сайжруулсан холболтын онцлогийг идэвхжүүлдэг."</string>
     <string name="enable_terminal_title" msgid="3834790541986303654">"Локал терминал"</string>
     <string name="enable_terminal_summary" msgid="2481074834856064500">"Локал суурьт хандалт хийх боломж олгодог терминалын апп-г идэвхжүүлэх"</string>
-    <!-- no translation found for enable_linux_terminal_title (5076044866895670637) -->
-    <skip />
-    <!-- no translation found for enable_linux_terminal_summary (5893216510985145320) -->
-    <skip />
+    <string name="enable_linux_terminal_title" msgid="5076044866895670637">"Linux-н хөгжүүлэлтийн орчин"</string>
+    <string name="enable_linux_terminal_summary" msgid="5893216510985145320">"Android дээр Linux терминалыг ажиллуулах"</string>
     <string name="hdcp_checking_title" msgid="3155692785074095986">"HDCP шалгах"</string>
     <string name="hdcp_checking_dialog_title" msgid="7691060297616217781">"HDCP шалгах авирыг тохируулах"</string>
     <string name="debug_debugging_category" msgid="535341063709248842">"Дебаг"</string>
diff --git a/packages/SettingsLib/res/values-nb/strings.xml b/packages/SettingsLib/res/values-nb/strings.xml
index 10c0e21..e16fea2 100644
--- a/packages/SettingsLib/res/values-nb/strings.xml
+++ b/packages/SettingsLib/res/values-nb/strings.xml
@@ -520,7 +520,7 @@
     <string name="battery_info_status_charging_fast_v2" msgid="1825439848151256589">"Hurtiglading"</string>
     <string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Kontrollert av administratoren"</string>
     <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Kontrollert av en begrenset innstilling"</string>
-    <string name="disabled" msgid="8017887509554714950">"Slått av"</string>
+    <string name="disabled" msgid="8017887509554714950">"Deaktivert"</string>
     <string name="external_source_trusted" msgid="1146522036773132905">"Tillatt"</string>
     <string name="external_source_untrusted" msgid="5037891688911672227">"Ikke tillatt"</string>
     <string name="install_other_apps" msgid="3232595082023199454">"Installer ukjente apper"</string>
diff --git a/packages/SettingsLib/res/values-or/strings.xml b/packages/SettingsLib/res/values-or/strings.xml
index fb3a166..73b0996 100644
--- a/packages/SettingsLib/res/values-or/strings.xml
+++ b/packages/SettingsLib/res/values-or/strings.xml
@@ -357,10 +357,8 @@
     <string name="enhanced_connectivity_summary" msgid="1576414159820676330">"ଏନହାନ୍ସଡ୍ କନେକ୍ଟିଭିଟି ଫିଚର୍ ସକ୍ଷମ କରିଥାଏ।"</string>
     <string name="enable_terminal_title" msgid="3834790541986303654">"ସ୍ଥାନୀୟ ଟର୍ମିନାଲ୍‌"</string>
     <string name="enable_terminal_summary" msgid="2481074834856064500">"ସ୍ଥାନୀୟ ଶେଲ୍‌କୁ ଆକ‌ସେସ୍‌ ଦେଉଥିବା ଟର୍ମିନଲ୍‌ ଆପ୍‌କୁ ସକ୍ଷମ କରନ୍ତୁ"</string>
-    <!-- no translation found for enable_linux_terminal_title (5076044866895670637) -->
-    <skip />
-    <!-- no translation found for enable_linux_terminal_summary (5893216510985145320) -->
-    <skip />
+    <string name="enable_linux_terminal_title" msgid="5076044866895670637">"Linux ଡେଭେଲପମେଣ୍ଟର ପରିବେଶ"</string>
+    <string name="enable_linux_terminal_summary" msgid="5893216510985145320">"Androidରେ Linux ଟର୍ମିନାଲ ଚାଲୁ କରନ୍ତୁ"</string>
     <string name="hdcp_checking_title" msgid="3155692785074095986">"HDCP ଯାଞ୍ଚ"</string>
     <string name="hdcp_checking_dialog_title" msgid="7691060297616217781">"HDCPର ଯାଞ୍ଚ ଗତିବିଧି ସେଟ୍‍ କରନ୍ତୁ"</string>
     <string name="debug_debugging_category" msgid="535341063709248842">"ଡିବଗ୍‌ କରୁଛି"</string>
diff --git a/packages/SettingsLib/res/values-pa/strings.xml b/packages/SettingsLib/res/values-pa/strings.xml
index 06a17a3..c1ef613 100644
--- a/packages/SettingsLib/res/values-pa/strings.xml
+++ b/packages/SettingsLib/res/values-pa/strings.xml
@@ -357,10 +357,8 @@
     <string name="enhanced_connectivity_summary" msgid="1576414159820676330">"ਵਿਸਤ੍ਰਿਤ ਕਨੈਕਟੀਵਿਟੀ ਵਿਸ਼ੇਸ਼ਤਾ ਨੂੰ ਚਾਲੂ ਕਰਦਾ ਹੈ।"</string>
     <string name="enable_terminal_title" msgid="3834790541986303654">"ਸਥਾਨਕ ਟਰਮੀਨਲ"</string>
     <string name="enable_terminal_summary" msgid="2481074834856064500">"ਟਰਮੀਨਲ ਐਪ ਨੂੰ ਚਾਲੂ ਕਰੋ ਜੋ ਸਥਾਨਕ ਸ਼ੈਲ ਪਹੁੰਚ ਪੇਸ਼ਕਸ਼ ਕਰਦਾ ਹੈ"</string>
-    <!-- no translation found for enable_linux_terminal_title (5076044866895670637) -->
-    <skip />
-    <!-- no translation found for enable_linux_terminal_summary (5893216510985145320) -->
-    <skip />
+    <string name="enable_linux_terminal_title" msgid="5076044866895670637">"Linux ਵਿਕਾਸ ਵਾਤਾਵਰਨ"</string>
+    <string name="enable_linux_terminal_summary" msgid="5893216510985145320">"Android \'ਤੇ Linux ਦੀ ਟਰਮੀਨਲ ਐਪ ਚਲਾਓ"</string>
     <string name="hdcp_checking_title" msgid="3155692785074095986">"HDCP ਜਾਂਚ"</string>
     <string name="hdcp_checking_dialog_title" msgid="7691060297616217781">"HDCP ਜਾਂਚ ਵਿਵਹਾਰ ਸੈੱਟ ਕਰੋ"</string>
     <string name="debug_debugging_category" msgid="535341063709248842">"ਡੀਬੱਗਿੰਗ"</string>
diff --git a/packages/SettingsLib/res/values-si/strings.xml b/packages/SettingsLib/res/values-si/strings.xml
index 2764ed2..a9004d0 100644
--- a/packages/SettingsLib/res/values-si/strings.xml
+++ b/packages/SettingsLib/res/values-si/strings.xml
@@ -357,10 +357,8 @@
     <string name="enhanced_connectivity_summary" msgid="1576414159820676330">"වැඩිදියුණු කළ සබැඳුම් හැකියා විශේෂාංගය සබල කරයි."</string>
     <string name="enable_terminal_title" msgid="3834790541986303654">"අභ්‍යන්තර අන්තය"</string>
     <string name="enable_terminal_summary" msgid="2481074834856064500">"දේශීය ෂෙල් ප්‍රවේශනය පිරිනමන ටර්මිනල් යෙදුම සබල කරන්න"</string>
-    <!-- no translation found for enable_linux_terminal_title (5076044866895670637) -->
-    <skip />
-    <!-- no translation found for enable_linux_terminal_summary (5893216510985145320) -->
-    <skip />
+    <string name="enable_linux_terminal_title" msgid="5076044866895670637">"Linux සංවර්ධන පරිසරය"</string>
+    <string name="enable_linux_terminal_summary" msgid="5893216510985145320">"Android මත Linux ටර්මිනලය ධාවනය කරන්න"</string>
     <string name="hdcp_checking_title" msgid="3155692785074095986">"HDCP පරික්ෂාව"</string>
     <string name="hdcp_checking_dialog_title" msgid="7691060297616217781">"HDCP පරික්ෂා හැසිරීම සකසන්න"</string>
     <string name="debug_debugging_category" msgid="535341063709248842">"නිදොස්කරණය"</string>
diff --git a/packages/SettingsLib/res/values-sq/strings.xml b/packages/SettingsLib/res/values-sq/strings.xml
index e26d428..0b39551 100644
--- a/packages/SettingsLib/res/values-sq/strings.xml
+++ b/packages/SettingsLib/res/values-sq/strings.xml
@@ -357,10 +357,8 @@
     <string name="enhanced_connectivity_summary" msgid="1576414159820676330">"Aktivizon veçorinë e \"Lidhshmërisë së përmirësuar\"."</string>
     <string name="enable_terminal_title" msgid="3834790541986303654">"Terminali lokal"</string>
     <string name="enable_terminal_summary" msgid="2481074834856064500">"Aktivizo aplikacionin terminal që ofron qasje në guaskën lokale"</string>
-    <!-- no translation found for enable_linux_terminal_title (5076044866895670637) -->
-    <skip />
-    <!-- no translation found for enable_linux_terminal_summary (5893216510985145320) -->
-    <skip />
+    <string name="enable_linux_terminal_title" msgid="5076044866895670637">"Ambienti i zhvillimit për Linux"</string>
+    <string name="enable_linux_terminal_summary" msgid="5893216510985145320">"Ekzekuto terminalin e Linux në Android"</string>
     <string name="hdcp_checking_title" msgid="3155692785074095986">"Kontrolli HDCP"</string>
     <string name="hdcp_checking_dialog_title" msgid="7691060297616217781">"Cakto kontrollin e HDCP-së"</string>
     <string name="debug_debugging_category" msgid="535341063709248842">"Korrigjimi i gabimeve"</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
index 8845d2e..8641f70 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -322,18 +322,27 @@
     }
 
     private void updatePreferredTransport() {
-        if (mProfiles.stream().noneMatch(p -> p instanceof LeAudioProfile)
-                || mProfiles.stream().noneMatch(p -> p instanceof HidProfile)) {
+        LeAudioProfile leAudioProfile =
+                (LeAudioProfile)
+                        mProfiles.stream()
+                                .filter(p -> p instanceof LeAudioProfile)
+                                .findFirst()
+                                .orElse(null);
+        HidProfile hidProfile =
+                (HidProfile)
+                        mProfiles.stream()
+                                .filter(p -> p instanceof HidProfile)
+                                .findFirst()
+                                .orElse(null);
+        if (leAudioProfile == null || hidProfile == null) {
             return;
         }
         // Both LeAudioProfile and HidProfile are connectable.
-        if (!mProfileManager
-                .getHidProfile()
-                .setPreferredTransport(
-                        mDevice,
-                        mProfileManager.getLeAudioProfile().isEnabled(mDevice)
-                                ? BluetoothDevice.TRANSPORT_LE
-                                : BluetoothDevice.TRANSPORT_BREDR)) {
+        if (!hidProfile.setPreferredTransport(
+                mDevice,
+                leAudioProfile.isEnabled(mDevice)
+                        ? BluetoothDevice.TRANSPORT_LE
+                        : BluetoothDevice.TRANSPORT_BREDR)) {
             Log.w(TAG, "Fail to set preferred transport");
         }
     }
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingServiceConnection.kt b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingServiceConnection.kt
index c16366e..edd49c5 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingServiceConnection.kt
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingServiceConnection.kt
@@ -296,6 +296,7 @@
                 val listener =
                     object : IDeviceSettingsListener.Stub() {
                         override fun onDeviceSettingsChanged(settings: List<DeviceSetting>) {
+                            Log.i(TAG, "Receive setting ids ${settings.map { it.settingId }}")
                             launch { send(settings) }
                         }
                     }
diff --git a/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlLoaderCompat.java b/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlLoaderCompat.java
index 121f549..3696000 100644
--- a/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlLoaderCompat.java
+++ b/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlLoaderCompat.java
@@ -41,6 +41,7 @@
             "/system_ext/etc/NOTICE.xml.gz",
             "/vendor_dlkm/etc/NOTICE.xml.gz",
             "/odm_dlkm/etc/NOTICE.xml.gz",
+            "/system_dlkm/etc/NOTICE.xml.gz",
     };
     static final String NOTICE_HTML_FILE_NAME = "NOTICE.html";
 
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
index 70cb2ef..30f8a79 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
@@ -1952,9 +1952,6 @@
 
     @Test
     public void leAudioHidDevice_leAudioEnabled_setPreferredTransportToLE() {
-
-        when(mProfileManager.getHidProfile()).thenReturn(mHidProfile);
-        when(mProfileManager.getLeAudioProfile()).thenReturn(mLeAudioProfile);
         when(mLeAudioProfile.isEnabled(mDevice)).thenReturn(true);
 
         updateProfileStatus(mHidProfile, BluetoothProfile.STATE_CONNECTED);
@@ -1965,8 +1962,6 @@
 
     @Test
     public void leAudioHidDevice_leAudioDisabled_setPreferredTransportToBredr() {
-        when(mProfileManager.getHidProfile()).thenReturn(mHidProfile);
-        when(mProfileManager.getLeAudioProfile()).thenReturn(mLeAudioProfile);
         when(mLeAudioProfile.isEnabled(mDevice)).thenReturn(false);
 
         updateProfileStatus(mLeAudioProfile, BluetoothProfile.STATE_CONNECTED);
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
index d3ee400..064198f 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
@@ -133,6 +133,7 @@
         Settings.Secure.FINGERPRINT_SIDE_FPS_ENROLL_TAP_WINDOW,
         Settings.Secure.FINGERPRINT_SIDE_FPS_AUTH_DOWNTIME,
         Settings.Secure.SFPS_PERFORMANT_AUTH_ENABLED,
+        Settings.Secure.SCREEN_OFF_UNLOCK_UDFPS_ENABLED,
         Settings.Secure.ACTIVE_UNLOCK_ON_WAKE,
         Settings.Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT,
         Settings.Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT_LEGACY,
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
index d34ccc5..c002a04 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
@@ -190,6 +190,7 @@
                 NON_NEGATIVE_INTEGER_VALIDATOR);
         VALIDATORS.put(Secure.FINGERPRINT_SIDE_FPS_AUTH_DOWNTIME, NON_NEGATIVE_INTEGER_VALIDATOR);
         VALIDATORS.put(Secure.SFPS_PERFORMANT_AUTH_ENABLED, BOOLEAN_VALIDATOR);
+        VALIDATORS.put(Secure.SCREEN_OFF_UNLOCK_UDFPS_ENABLED, BOOLEAN_VALIDATOR);
         VALIDATORS.put(Secure.SHOW_MEDIA_WHEN_BYPASSING, BOOLEAN_VALIDATOR);
         VALIDATORS.put(Secure.FACE_UNLOCK_APP_ENABLED, BOOLEAN_VALIDATOR);
         VALIDATORS.put(Secure.FACE_UNLOCK_ALWAYS_REQUIRE_CONFIRMATION, BOOLEAN_VALIDATOR);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index a8af43f5..603a911 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -2479,10 +2479,10 @@
         final long identity = Binder.clearCallingIdentity();
         try {
             int currentUser = ActivityManager.getCurrentUser();
-            if (callingUser == currentUser) {
-                // enforce the deny list only if the caller is not current user. Currently only auto
-                // uses background visible user, and auto doesn't support profiles so profiles of
-                // current users is not checked here.
+            if (callingUser == currentUser || callingUser == UserHandle.USER_SYSTEM) {
+                // enforce the deny list only if the caller is not current user or not a system
+                // user. Currently only auto uses background visible user, and auto doesn't
+                // support profiles so profiles of current users is not checked here.
                 return;
             }
         } finally {
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index 5f90b39..a18b6c1 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -97,6 +97,7 @@
         "tests/src/**/systemui/media/dialog/MediaOutputBroadcastDialogTest.java",
         "tests/src/**/systemui/media/dialog/MediaOutputDialogTest.java",
         "tests/src/**/systemui/mediaprojection/permission/MediaProjectionPermissionDialogDelegateTest.kt",
+        "tests/src/**/systemui/settings/brightness/BrightnessDialogTest.kt",
     ],
 }
 
@@ -106,6 +107,7 @@
     srcs: [
         "tests/src/**/systemui/ExpandHelperTest.java",
         "tests/src/**/AAAPlusPlusVerifySysuiRequiredTestPropertiesTest.java",
+        "tests/src/**/systemui/accessibility/AccessibilityGestureTargetsObserverTest.java",
         "tests/src/**/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuControllerTest.java",
         "tests/src/**/systemui/accessibility/floatingmenu/AccessibilityTargetAdapterTest.java",
         "tests/src/**/systemui/screenshot/appclips/AppClipsActivityTest.java",
@@ -269,6 +271,42 @@
         "tests/src/**/systemui/volume/VolumeDialogImplTest.java",
         "tests/src/**/systemui/wallet/controller/QuickAccessWalletControllerTest.java",
         "tests/src/**/systemui/wallet/ui/WalletScreenControllerTest.java",
+        "tests/src/**/systemui/accessibility/data/repository/AccessibilityQsShortcutsRepositoryImplForDeviceTest.kt",
+        "tests/src/**/systemui/biometrics/UdfpsControllerOverlayTest.kt",
+        // TODO(b/322324387): Fails to start due to missing ScreenshotActivity
+        "tests/src/**/systemui/bouncer/ui/composable/BouncerContentTest.kt",
+        "tests/src/**/systemui/bouncer/ui/composable/PatternBouncerTest.kt",
+        "tests/src/**/systemui/clipboardoverlay/ClipboardListenerTest.java",
+        "tests/src/**/systemui/communal/data/db/CommunalDatabaseMigrationsTest.kt",
+        "tests/src/**/systemui/communal/data/db/CommunalWidgetDaoTest.kt",
+        "tests/src/**/systemui/display/data/repository/DisplayWindowPropertiesRepositoryImplTest.kt",
+        "tests/src/**/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModelTest.kt",
+        "tests/src/**/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt",
+        "tests/src/**/systemui/lifecycle/ActivatableTest.kt",
+        "tests/src/**/systemui/lifecycle/HydratorTest.kt",
+        "tests/src/**/systemui/media/dialog/MediaSwitchingControllerTest.java",
+        "tests/src/**/systemui/qs/QSImplTest.java",
+        "tests/src/**/systemui/qs/panels/ui/compose/DragAndDropTest.kt",
+        "tests/src/**/systemui/qs/panels/ui/compose/ResizingTest.kt",
+        "tests/src/**/systemui/screenshot/ActionExecutorTest.kt",
+        "tests/src/**/systemui/screenshot/ScreenshotDetectionControllerTest.kt",
+        "tests/src/**/systemui/screenshot/TakeScreenshotServiceTest.kt",
+        "tests/src/**/systemui/accessibility/floatingmenu/MenuAnimationControllerTest.java",
+        "tests/src/**/systemui/accessibility/floatingmenu/PositionTest.java",
+        "tests/src/**/systemui/animation/TransitionAnimatorTest.kt",
+        "tests/src/**/systemui/screenshot/scroll/ScrollCaptureControllerTest.java",
+        "tests/src/**/systemui/lifecycle/SysuiViewModelTest.kt",
+        "tests/src/**/systemui/flags/FakeFeatureFlags.kt",
+        "tests/src/**/systemui/animation/TransitionAnimatorTest.kt",
+        "tests/src/**/systemui/shade/carrier/ShadeCarrierGroupControllerTest.java",
+        "tests/src/**/systemui/statusbar/connectivity/NetworkControllerSignalTest.java",
+        "tests/src/**/systemui/statusbar/notification/row/BigPictureIconManagerTest.kt",
+        "tests/src/**/systemui/statusbar/policy/RotationLockControllerImplTest.java",
+        "tests/src/**/systemui/statusbar/phone/ScrimControllerTest.java",
+        "tests/src/**/systemui/stylus/StylusUsiPowerStartableTest.kt",
+        "tests/src/**/systemui/toast/ToastUITest.java",
+        "tests/src/**/systemui/statusbar/policy/FlashlightControllerImplTest.kt",
+        "tests/src/**/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImplTest.kt",
     ],
 }
 
@@ -276,42 +314,23 @@
 filegroup {
     name: "SystemUI-tests-broken-robofiles-compile",
     srcs: [
+        "tests/src/**/keyguard/KeyguardUpdateMonitorTest.java",
+        "tests/src/**/systemui/statusbar/notification/icon/IconManagerTest.kt",
+        "tests/src/**/systemui/statusbar/KeyguardIndicationControllerTest.java",
+        "tests/src/**/systemui/doze/DozeScreenStateTest.java",
+        "tests/src/**/keyguard/CarrierTextManagerTest.java",
+        "tests/src/**/systemui/notetask/NoteTaskInitializerTest.kt",
+        "tests/src/**/systemui/media/controls/ui/controller/MediaHierarchyManagerTest.kt",
         "tests/src/**/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt",
-        "tests/src/**/systemui/accessibility/data/repository/AccessibilityQsShortcutsRepositoryImplForDeviceTest.kt",
-        "tests/src/**/systemui/biometrics/AuthDialogPanelInteractionDetectorTest.kt",
         "tests/src/**/systemui/controls/management/ControlsFavoritingActivityTest.kt",
         "tests/src/**/systemui/controls/management/ControlsProviderSelectorActivityTest.kt",
-        "tests/src/**/systemui/controls/start/ControlsStartableTest.kt",
-        "tests/src/**/systemui/haptics/slider/SliderStateTrackerTest.kt",
-        "tests/src/**/systemui/keyboard/stickykeys/ui/StickyKeysIndicatorCoordinatorTest.kt",
-        "tests/src/**/systemui/keyboard/stickykeys/ui/viewmodel/StickyKeysIndicatorViewModelTest.kt",
-        "tests/src/**/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceCoreStartableTest.kt",
-        "tests/src/**/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt",
-        "tests/src/**/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorSceneContainerTest.kt",
-        "tests/src/**/systemui/keyguard/ResourceTrimmerTest.kt",
-        "tests/src/**/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt",
         "tests/src/**/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt",
-        "tests/src/**/systemui/media/controls/ui/controller/MediaHierarchyManagerTest.kt",
-        "tests/src/**/systemui/mediaprojection/taskswitcher/MediaProjectionTaskSwitcherCoreStartableTest.kt",
+        "tests/src/**/systemui/media/controls/domain/pipeline/MediaTimeoutListenerTest.kt",
         "tests/src/**/systemui/media/taptotransfer/receiver/FakeMediaTttChipControllerReceiver.kt",
         "tests/src/**/systemui/qs/tileimpl/QSTileViewImplTest.kt",
-        "tests/src/**/systemui/qs/tiles/DeviceControlsTileTest.kt",
-        "tests/src/**/systemui/screenshot/ActionExecutorTest.kt",
-        "tests/src/**/systemui/screenshot/ActionIntentCreatorTest.kt",
-        "tests/src/**/systemui/screenshot/DefaultScreenshotActionsProviderTest.kt",
-        "tests/src/**/systemui/screenshot/TakeScreenshotServiceTest.kt",
-        "tests/src/**/systemui/statusbar/notification/collection/TargetSdkResolverTest.kt",
-        "tests/src/**/systemui/statusbar/notification/icon/IconManagerTest.kt",
-        "tests/src/**/systemui/statusbar/notification/row/BigPictureIconManagerTest.kt",
-        "tests/src/**/systemui/statusbar/notification/row/NotificationSettingsControllerTest.kt",
-        "tests/src/**/systemui/statusbar/notification/stack/ui/view/NotificationStatsLoggerTest.kt",
-        "tests/src/**/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImplTest.kt",
-        "tests/src/**/systemui/statusbar/policy/FlashlightControllerImplTest.kt",
-        "tests/src/**/systemui/statusbar/policy/SensitiveNotificationProtectionControllerFlagDisabledTest.kt",
-        "tests/src/**/systemui/stylus/StylusUsiPowerStartableTest.kt",
+        "tests/src/**/systemui/statusbar/policy/BatteryStateNotifierTest.kt",
         "tests/src/**/systemui/temporarydisplay/TemporaryViewDisplayControllerTest.kt",
         "tests/src/**/keyguard/ClockEventControllerTest.kt",
-        "tests/src/**/systemui/animation/TransitionAnimatorTest.kt",
         "tests/src/**/systemui/bluetooth/qsdialog/BluetoothAutoOnRepositoryTest.kt",
         "tests/src/**/systemui/bluetooth/qsdialog/BluetoothStateInteractorTest.kt",
         "tests/src/**/systemui/bluetooth/qsdialog/BluetoothTileDialogDelegateTest.kt",
@@ -319,9 +338,6 @@
         "tests/src/**/systemui/bluetooth/qsdialog/BluetoothTileDialogViewModelTest.kt",
         "tests/src/**/systemui/bluetooth/qsdialog/DeviceItemFactoryTest.kt",
         "tests/src/**/systemui/bluetooth/qsdialog/DeviceItemInteractorTest.kt",
-        // TODO(b/322324387): Fails to start due to missing ScreenshotActivity
-        "tests/src/**/systemui/bouncer/ui/composable/BouncerContentTest.kt",
-        "tests/src/**/systemui/bouncer/ui/composable/PatternBouncerTest.kt",
         "tests/src/**/systemui/broadcast/UserBroadcastDispatcherTest.kt",
         "tests/src/**/systemui/charging/WiredChargingRippleControllerTest.kt",
         "tests/src/**/systemui/clipboardoverlay/ClipboardModelTest.kt",
@@ -348,8 +364,6 @@
         "tests/src/**/systemui/media/taptotransfer/sender/MediaTttSenderCoordinatorTest.kt",
         "tests/src/**/systemui/navigationbar/gestural/BackPanelControllerTest.kt",
         "tests/src/**/systemui/notetask/NoteTaskControllerTest.kt",
-        "tests/src/**/systemui/notetask/NoteTaskInitializerTest.kt",
-        "tests/src/**/systemui/power/domain/interactor/PowerInteractorTest.kt",
         "tests/src/**/systemui/privacy/AppOpsPrivacyItemMonitorTest.kt",
         "tests/src/**/systemui/privacy/PrivacyItemControllerTest.kt",
         "tests/src/**/systemui/qs/external/CustomTileStatePersisterTest.kt",
@@ -376,7 +390,6 @@
         "tests/src/**/systemui/statusbar/LightRevealScrimTest.kt",
         "tests/src/**/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt",
         "tests/src/**/systemui/statusbar/notification/collection/coordinator/DataStoreCoordinatorTest.kt",
-        "tests/src/**/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.kt",
         "tests/src/**/systemui/statusbar/notification/collection/coordinator/RowAppearanceCoordinatorTest.kt",
         "tests/src/**/systemui/statusbar/notification/collection/coordinator/ShadeEventCoordinatorTest.kt",
         "tests/src/**/systemui/statusbar/notification/collection/coordinator/StackCoordinatorTest.kt",
@@ -406,8 +419,6 @@
         "tests/src/**/systemui/statusbar/SplitShadeLockScreenOverScrollerTest.kt",
         "tests/src/**/systemui/stylus/StylusUsiPowerUiTest.kt",
         "tests/src/**/systemui/temporarydisplay/chipbar/ChipbarCoordinatorTest.kt",
-        "tests/src/**/keyguard/KeyguardUpdateMonitorTest.java",
-        "tests/src/**/keyguard/CarrierTextManagerTest.java",
         "tests/src/**/systemui/ScreenDecorationsTest.java",
         "tests/src/**/systemui/temporarydisplay/chipbar/SwipeChipbarAwayGestureHandlerTest.kt",
         "tests/src/**/systemui/media/controls/domain/pipeline/MediaDataProcessorTest.kt",
@@ -431,33 +442,17 @@
         "tests/src/**/systemui/statusbar/policy/BatteryControllerStartableTest.java",
         "tests/src/**/systemui/statusbar/policy/BatteryControllerTest.java",
         "tests/src/**/systemui/statusbar/policy/SensitiveNotificationProtectionControllerTest.kt",
-        "tests/src/**/systemui/statusbar/notification/NotificationSectionsFeatureManagerTest.kt",
         "tests/src/**/systemui/statusbar/pipeline/mobile/data/repository/CarrierConfigRepositoryTest.kt",
         "tests/src/**/systemui/statusbar/KeyboardShortcutsReceiverTest.java",
         "tests/src/**/systemui/wmshell/BubblesTest.java",
-        "tests/src/**/systemui/biometrics/AuthRippleControllerTest.kt",
-        "tests/src/**/keyguard/KeyguardAbsKeyInputViewControllerTest.java",
-        "tests/src/**/systemui/accessibility/floatingmenu/MenuAnimationControllerTest.java",
-        "tests/src/**/systemui/clipboardoverlay/ClipboardListenerTest.java",
-        "tests/src/**/systemui/doze/DozeScreenStateTest.java",
-        "tests/src/**/systemui/keyguard/WorkLockActivityControllerTest.java",
-        "tests/src/**/systemui/media/dialog/MediaSwitchingControllerTest.java",
-        "tests/src/**/systemui/navigationbar/views/NavigationBarTest.java",
-        "tests/src/**/systemui/power/PowerNotificationWarningsTest.java",
-        "tests/src/**/systemui/qs/QSFooterViewControllerTest.java",
-        "tests/src/**/systemui/qs/QSImplTest.java",
-        "tests/src/**/systemui/qs/tiles/QuickAccessWalletTileTest.java",
-        "tests/src/**/systemui/shade/carrier/ShadeCarrierGroupControllerTest.java",
-        "tests/src/**/systemui/statusbar/connectivity/NetworkControllerBaseTest.java",
-        "tests/src/**/systemui/statusbar/connectivity/NetworkControllerDataTest.java",
-        "tests/src/**/systemui/statusbar/connectivity/NetworkControllerEthernetTest.java",
-        "tests/src/**/systemui/statusbar/connectivity/NetworkControllerSignalTest.java",
-        "tests/src/**/systemui/statusbar/connectivity/NetworkControllerWifiTest.java",
-        "tests/src/**/systemui/statusbar/KeyguardIndicationControllerTest.java",
-        "tests/src/**/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java",
-        "tests/src/**/systemui/statusbar/phone/ScrimControllerTest.java",
-        "tests/src/**/systemui/statusbar/policy/RotationLockControllerImplTest.java",
-        "tests/src/**/systemui/toast/ToastUITest.java",
+        "tests/src/**/systemui/power/PowerUITest.java",
+        "tests/src/**/systemui/qs/QSSecurityFooterTest.java",
+        "tests/src/**/systemui/qs/tileimpl/QSTileImplTest.java",
+        "tests/src/**/systemui/shared/plugins/PluginActionManagerTest.java",
+        "tests/src/**/systemui/statusbar/CommandQueueTest.java",
+        "tests/src/**/systemui/statusbar/connectivity/CallbackHandlerTest.java",
+        "tests/src/**/systemui/statusbar/policy/SecurityControllerTest.java",
+        "tests/src/**/systemui/shared/clocks/view/SimpleDigitalClockTextViewTest.kt",
     ],
     visibility: ["//visibility:private"],
 }
@@ -499,6 +494,7 @@
         },
     },
     use_resource_processor: true,
+    resource_dirs: [],
     static_libs: [
         "//frameworks/libs/systemui:compilelib",
         "SystemUI-res",
@@ -885,6 +881,7 @@
         "android.test.base.impl",
         "android.test.mock.impl",
         "truth",
+        "androidx.test.ext.truth",
     ],
 
     upstream: true,
@@ -922,6 +919,7 @@
         "android.test.base.impl",
         "android.test.mock.impl",
         "truth",
+        "androidx.test.ext.truth",
     ],
 
     upstream: true,
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 510c9b7..4531b79 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -489,7 +489,6 @@
         <activity android:name=".touchpad.tutorial.ui.view.TouchpadTutorialActivity"
             android:exported="true"
             android:showForAllUsers="true"
-            android:screenOrientation="userLandscape"
             android:theme="@style/Theme.AppCompat.NoActionBar">
             <intent-filter>
                 <action android:name="com.android.systemui.action.TOUCHPAD_TUTORIAL"/>
@@ -500,7 +499,6 @@
         <activity android:name=".inputdevice.tutorial.ui.view.KeyboardTouchpadTutorialActivity"
             android:exported="true"
             android:showForAllUsers="true"
-            android:screenOrientation="userLandscape"
             android:theme="@style/Theme.AppCompat.NoActionBar">
             <intent-filter>
                 <action android:name="com.android.systemui.action.TOUCHPAD_KEYBOARD_TUTORIAL"/>
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/values-sk/strings.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/values-sk/strings.xml
index 3c92f83..70de5a7 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/res/values-sk/strings.xml
+++ b/packages/SystemUI/accessibility/accessibilitymenu/res/values-sk/strings.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="accessibility_menu_service_name" msgid="730136711554740131">"Ponuka Dostupnosť"</string>
+    <string name="accessibility_menu_service_name" msgid="730136711554740131">"Ponuka dostupnosti"</string>
     <string name="accessibility_menu_intro" msgid="3164193281544042394">"Ponukou Dostupnosť sa rozumie veľká ponuka na obrazovke, pomocou ktorej môžete ovládať zariadenie. Môžete ho uzamknúť, ovládať hlasitosť a jas, vytvárať snímky obrazovky a mnoho ďalšieho."</string>
     <string name="assistant_label" msgid="6796392082252272356">"Asistent"</string>
     <string name="assistant_utterance" msgid="65509599221141377">"Asistent"</string>
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index 2278789..d50a92b 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -33,6 +33,13 @@
 }
 
 flag {
+  name: "notifications_redesign_footer_view"
+  namespace: "systemui"
+  description: "Notifications Redesign: Update the look of the notifications footer."
+  bug: "375010573"
+}
+
+flag {
    name: "notification_row_content_binder_refactor"
    namespace: "systemui"
    description: "Convert the NotificationContentInflater to Kotlin and restructure it to support modern views"
@@ -139,6 +146,13 @@
 }
 
 flag {
+    name: "notifications_dismiss_pruned_summaries"
+    namespace: "systemui"
+    description: "NotifCollection.dismissNotifications will now dismiss summaries that are pruned from the shade."
+    bug: "355967751"
+}
+
+flag {
    name: "notification_transparent_header_fix"
    namespace: "systemui"
    description: "fix the transparent group header issue for async header inflation."
@@ -364,6 +378,17 @@
 }
 
 flag {
+    name: "status_bar_show_audio_only_projection_chip"
+    namespace: "systemui"
+    description: "Show chip on the left side of the status bar when a user is only sharing *audio* "
+        "during a media projection"
+    bug: "373308507"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
+
+flag {
     name: "status_bar_use_repos_for_call_chip"
     namespace: "systemui"
     description: "Use repositories as the source of truth for call notifications shown as a chip in"
@@ -715,6 +740,13 @@
 }
 
 flag {
+    name: "smartspace_swipe_event_logging"
+    namespace: "systemui"
+    description: "Log card swipe events in smartspace"
+    bug: "374150422"
+}
+
+flag {
    name: "pin_input_field_styled_focus_state"
    namespace: "systemui"
    description: "Enables styled focus states on pin input field if keyboard is connected"
@@ -977,6 +1009,16 @@
 }
 
 flag {
+    name: "shortcut_helper_key_glyph"
+    namespace: "systemui"
+    description: "Allow showing key glyph in shortcut helper"
+    bug: "353902478"
+    metadata {
+      purpose: PURPOSE_BUGFIX
+    }
+}
+
+flag {
   name: "dream_overlay_bouncer_swipe_direction_filtering"
   namespace: "systemui"
   description: "do not initiate bouncer swipe when the direction is opposite of the expansion"
@@ -1104,6 +1146,13 @@
 }
 
 flag {
+  name: "communal_standalone_support"
+  namespace: "systemui"
+  description: "Support communal features without a dock"
+  bug: "352301247"
+}
+
+flag {
     name: "dream_overlay_updated_font"
     namespace: "systemui"
     description: "Flag to enable updated font settings for dream overlay"
@@ -1546,6 +1595,16 @@
 }
 
 flag {
+  name: "transition_race_condition"
+  namespace: "systemui"
+  description: "Thread-safe keyguard transitions"
+  bug: "358533338"
+  metadata {
+       purpose: PURPOSE_BUGFIX
+  }
+}
+
+flag {
    name: "media_projection_request_attribution_fix"
    namespace: "systemui"
    description: "Ensure MediaProjection consent requests are properly attributed"
@@ -1600,4 +1659,20 @@
   metadata {
     purpose: PURPOSE_BUGFIX
   }
-}
\ No newline at end of file
+}
+
+flag {
+  name: "shade_expands_on_status_bar_long_press"
+  namespace: "systemui"
+  description: "Expands the shade on long press of any status bar"
+  bug: "371224114"
+}
+
+
+flag {
+    name: "keyboard_shortcut_helper_shortcut_customizer"
+    namespace: "systemui"
+    description: "An implementation of shortcut customizations through shortcut helper."
+    bug: "365064144"
+}
+
diff --git a/packages/SystemUI/animation/lib/Android.bp b/packages/SystemUI/animation/lib/Android.bp
index d9230ec..7d73023 100644
--- a/packages/SystemUI/animation/lib/Android.bp
+++ b/packages/SystemUI/animation/lib/Android.bp
@@ -48,6 +48,19 @@
 }
 
 filegroup {
+    name: "PlatformAnimationLib-client-srcs",
+    srcs: [
+        "src/com/android/systemui/animation/OriginRemoteTransition.java",
+        "src/com/android/systemui/animation/OriginTransitionSession.java",
+        "src/com/android/systemui/animation/SurfaceUIComponent.java",
+        "src/com/android/systemui/animation/Transactions.java",
+        "src/com/android/systemui/animation/UIComponent.java",
+        "src/com/android/systemui/animation/ViewUIComponent.java",
+        ":PlatformAnimationLib-aidl",
+    ],
+}
+
+filegroup {
     name: "PlatformAnimationLib-aidl",
     srcs: [
         "src/**/*.aidl",
diff --git a/packages/SystemUI/animation/lib/OWNERS b/packages/SystemUI/animation/lib/OWNERS
new file mode 100644
index 0000000..7569419
--- /dev/null
+++ b/packages/SystemUI/animation/lib/OWNERS
@@ -0,0 +1,3 @@
+#inherits OWNERS from SystemUI in addition to WEAR framework owners below
+file:platform/frameworks/base:/WEAR_OWNERS
+
diff --git a/packages/SystemUI/animation/lib/src/com/android/systemui/animation/OriginRemoteTransition.java b/packages/SystemUI/animation/lib/src/com/android/systemui/animation/OriginRemoteTransition.java
index 08db95e..2b5ff7c 100644
--- a/packages/SystemUI/animation/lib/src/com/android/systemui/animation/OriginRemoteTransition.java
+++ b/packages/SystemUI/animation/lib/src/com/android/systemui/animation/OriginRemoteTransition.java
@@ -44,6 +44,7 @@
 /**
  * An implementation of {@link IRemoteTransition} that accepts a {@link UIComponent} as the origin
  * and automatically attaches it to the transition leash before the transition starts.
+ * @hide
  */
 public class OriginRemoteTransition extends IRemoteTransition.Stub {
     private static final String TAG = "OriginRemoteTransition";
@@ -328,7 +329,9 @@
                 /* baseBounds= */ maxBounds);
     }
 
-    /** An interface that represents an origin transitions. */
+    /** An interface that represents an origin transitions.
+     * @hide
+     */
     public interface TransitionPlayer {
 
         /**
diff --git a/packages/SystemUI/animation/lib/src/com/android/systemui/animation/OriginTransitionSession.java b/packages/SystemUI/animation/lib/src/com/android/systemui/animation/OriginTransitionSession.java
index 23693b6..6d6aa88 100644
--- a/packages/SystemUI/animation/lib/src/com/android/systemui/animation/OriginTransitionSession.java
+++ b/packages/SystemUI/animation/lib/src/com/android/systemui/animation/OriginTransitionSession.java
@@ -43,6 +43,7 @@
 /**
  * A session object that holds origin transition states for starting an activity from an on-screen
  * UI component and smoothly transitioning back from the activity to the same UI component.
+ * @hide
  */
 public class OriginTransitionSession {
     private static final String TAG = "OriginTransitionSession";
@@ -179,7 +180,10 @@
         }
     }
 
-    /** A builder to build a {@link OriginTransitionSession}. */
+    /**
+     * A builder to build a {@link OriginTransitionSession}.
+     * @hide
+     */
     public static class Builder {
         private final Context mContext;
         @Nullable private final IOriginTransitions mOriginTransitions;
diff --git a/packages/SystemUI/animation/lib/src/com/android/systemui/animation/SurfaceUIComponent.java b/packages/SystemUI/animation/lib/src/com/android/systemui/animation/SurfaceUIComponent.java
index 2438736..e1ff2a4 100644
--- a/packages/SystemUI/animation/lib/src/com/android/systemui/animation/SurfaceUIComponent.java
+++ b/packages/SystemUI/animation/lib/src/com/android/systemui/animation/SurfaceUIComponent.java
@@ -26,7 +26,10 @@
 import java.util.Collection;
 import java.util.concurrent.Executor;
 
-/** A {@link UIComponent} representing a {@link SurfaceControl}. */
+/**
+ * A {@link UIComponent} representing a {@link SurfaceControl}.
+ * @hide
+ */
 public class SurfaceUIComponent implements UIComponent {
     private final Collection<SurfaceControl> mSurfaces;
     private final Rect mBaseBounds;
@@ -89,7 +92,10 @@
                 + "}";
     }
 
-    /** A {@link Transaction} wrapping a {@link SurfaceControl.Transaction}. */
+    /**
+     * A {@link Transaction} wrapping a {@link SurfaceControl.Transaction}.
+     * @hide
+     */
     public static class Transaction implements UIComponent.Transaction<SurfaceUIComponent> {
         private final SurfaceControl.Transaction mTransaction;
         private final ArrayList<Runnable> mChanges = new ArrayList<>();
diff --git a/packages/SystemUI/animation/lib/src/com/android/systemui/animation/Transactions.java b/packages/SystemUI/animation/lib/src/com/android/systemui/animation/Transactions.java
index 5240d99..64d2172 100644
--- a/packages/SystemUI/animation/lib/src/com/android/systemui/animation/Transactions.java
+++ b/packages/SystemUI/animation/lib/src/com/android/systemui/animation/Transactions.java
@@ -27,6 +27,7 @@
 /**
  * A composite {@link UIComponent.Transaction} that combines multiple other transactions for each ui
  * type.
+ * @hide
  */
 public class Transactions implements UIComponent.Transaction<UIComponent> {
     private final Map<Class, UIComponent.Transaction> mTransactions = new ArrayMap<>();
diff --git a/packages/SystemUI/animation/lib/src/com/android/systemui/animation/UIComponent.java b/packages/SystemUI/animation/lib/src/com/android/systemui/animation/UIComponent.java
index 747e4d1..8aec2f4 100644
--- a/packages/SystemUI/animation/lib/src/com/android/systemui/animation/UIComponent.java
+++ b/packages/SystemUI/animation/lib/src/com/android/systemui/animation/UIComponent.java
@@ -17,12 +17,17 @@
 package com.android.systemui.animation;
 
 import android.annotation.FloatRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.graphics.Rect;
 import android.view.SurfaceControl;
 
 import java.util.concurrent.Executor;
 
-/** An interface representing an UI component on the display. */
+/**
+ * An interface representing an UI component on the display.
+ * @hide
+ */
 public interface UIComponent {
 
     /** Get the current alpha of this UI. */
@@ -32,39 +37,48 @@
     boolean isVisible();
 
     /** Get the bounds of this UI in its display. */
+    @NonNull
     Rect getBounds();
 
     /** Create a new {@link Transaction} that can update this UI. */
+    @NonNull
     Transaction newTransaction();
 
     /**
      * A transaction class for updating {@link UIComponent}.
      *
      * @param <T> the subtype of {@link UIComponent} that this {@link Transaction} can handle.
+     * @hide
      */
     interface Transaction<T extends UIComponent> {
         /** Update alpha of an UI. Execution will be delayed until {@link #commit()} is called. */
-        Transaction setAlpha(T ui, @FloatRange(from = 0.0, to = 1.0) float alpha);
+        Transaction setAlpha(@NonNull T ui, @FloatRange(from = 0.0, to = 1.0) float alpha);
 
         /**
          * Update visibility of an UI. Execution will be delayed until {@link #commit()} is called.
          */
-        Transaction setVisible(T ui, boolean visible);
+        @NonNull
+        Transaction setVisible(@NonNull T ui, boolean visible);
 
         /** Update bounds of an UI. Execution will be delayed until {@link #commit()} is called. */
-        Transaction setBounds(T ui, Rect bounds);
+        @NonNull
+        Transaction setBounds(@NonNull T ui, @NonNull Rect bounds);
 
         /**
          * Attach a ui to the transition leash. Execution will be delayed until {@link #commit()} is
          * called.
          */
-        Transaction attachToTransitionLeash(T ui, SurfaceControl transitionLeash, int w, int h);
+        @NonNull
+        Transaction attachToTransitionLeash(
+                @NonNull T ui, @NonNull SurfaceControl transitionLeash, int w, int h);
 
         /**
          * Detach a ui from the transition leash. Execution will be delayed until {@link #commit} is
          * called.
          */
-        Transaction detachFromTransitionLeash(T ui, Executor executor, Runnable onDone);
+        @NonNull
+        Transaction detachFromTransitionLeash(
+                @NonNull T ui, @NonNull Executor executor, @Nullable Runnable onDone);
 
         /** Commit any pending changes added to this transaction. */
         void commit();
diff --git a/packages/SystemUI/animation/lib/src/com/android/systemui/animation/ViewUIComponent.java b/packages/SystemUI/animation/lib/src/com/android/systemui/animation/ViewUIComponent.java
index 313789c..4c047d5 100644
--- a/packages/SystemUI/animation/lib/src/com/android/systemui/animation/ViewUIComponent.java
+++ b/packages/SystemUI/animation/lib/src/com/android/systemui/animation/ViewUIComponent.java
@@ -38,6 +38,7 @@
  * be changed to INVISIBLE in its view tree. This allows the {@link View} to transform in the
  * full-screen size leash without being constrained by the view tree's boundary or inheriting its
  * parent's alpha and transformation.
+ * @hide
  */
 public class ViewUIComponent implements UIComponent {
     private static final String TAG = "ViewUIComponent";
@@ -234,6 +235,9 @@
         mView.post(this::draw);
     }
 
+    /**
+     * @hide
+     */
     public static class Transaction implements UIComponent.Transaction<ViewUIComponent> {
         private final List<Runnable> mChanges = new ArrayList<>();
 
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/back/FlingOnBackAnimationCallback.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/back/FlingOnBackAnimationCallback.kt
index cd2dd04..47ad0b3 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/back/FlingOnBackAnimationCallback.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/back/FlingOnBackAnimationCallback.kt
@@ -95,16 +95,18 @@
     final override fun onBackProgressed(backEvent: BackEvent) {
         val interpolatedProgress = progressInterpolator.getInterpolation(backEvent.progress)
         if (predictiveBackTimestampApi()) {
-            velocityTracker.addMovement(
-                MotionEvent.obtain(
-                    /* downTime */ downTime!!,
-                    /* eventTime */ backEvent.frameTimeMillis,
-                    /* action */ ACTION_MOVE,
-                    /* x */ interpolatedProgress * SCALE_FACTOR,
-                    /* y */ 0f,
-                    /* metaState */ 0,
+            downTime?.let { downTime ->
+                velocityTracker.addMovement(
+                    MotionEvent.obtain(
+                        /* downTime */ downTime,
+                        /* eventTime */ backEvent.frameTimeMillis,
+                        /* action */ ACTION_MOVE,
+                        /* x */ interpolatedProgress * SCALE_FACTOR,
+                        /* y */ 0f,
+                        /* metaState */ 0,
+                    )
                 )
-            )
+            }
             lastBackEvent =
                 BackEvent(
                     backEvent.touchX,
diff --git a/packages/SystemUI/common/Android.bp b/packages/SystemUI/common/Android.bp
index 6fc13d7..91dc3e3 100644
--- a/packages/SystemUI/common/Android.bp
+++ b/packages/SystemUI/common/Android.bp
@@ -22,20 +22,13 @@
     default_applicable_licenses: ["frameworks_base_packages_SystemUI_license"],
 }
 
-android_library {
-
+java_library {
     name: "SystemUICommon",
-    use_resource_processor: true,
 
     srcs: [
         "src/**/*.java",
         "src/**/*.kt",
     ],
 
-    static_libs: [
-        "androidx.core_core-ktx",
-    ],
-
-    manifest: "AndroidManifest.xml",
     kotlincflags: ["-Xjvm-default=all"],
 }
diff --git a/packages/SystemUI/common/AndroidManifest.xml b/packages/SystemUI/common/AndroidManifest.xml
deleted file mode 100644
index 6f757eb..0000000
--- a/packages/SystemUI/common/AndroidManifest.xml
+++ /dev/null
@@ -1,21 +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.
--->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.android.systemui.common">
-
-</manifest>
diff --git a/packages/SystemUI/common/README.md b/packages/SystemUI/common/README.md
index 1cc5277..17e5412 100644
--- a/packages/SystemUI/common/README.md
+++ b/packages/SystemUI/common/README.md
@@ -1,5 +1,9 @@
 # SystemUICommon
 
-`SystemUICommon` is a module within SystemUI that hosts standalone helper libraries. It is intended to be used by other modules, and therefore should not have other SystemUI dependencies to avoid circular dependencies.
+`SystemUICommon` is a module within SystemUI that hosts standalone helper libraries. It is intended
+to be used by other modules, and therefore should not have other SystemUI dependencies to avoid
+circular dependencies.
 
-To maintain the structure of this module, please refrain from adding components at the top level. Instead, add them to specific sub-packages, such as `systemui/common/buffer/`. This will help to keep the module organized and easy to navigate.
+To maintain the structure of this module, please refrain from adding components at the top level.
+Instead, add them to specific sub-packages, such as `systemui/common/buffer/`. This will help to
+keep the module organized and easy to navigate.
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 d08df26..1a8c7f8 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
@@ -30,6 +30,8 @@
 import androidx.compose.foundation.Image
 import androidx.compose.foundation.background
 import androidx.compose.foundation.combinedClickable
+import androidx.compose.foundation.gestures.awaitEachGesture
+import androidx.compose.foundation.gestures.awaitFirstDown
 import androidx.compose.foundation.gestures.detectTapGestures
 import androidx.compose.foundation.layout.Arrangement
 import androidx.compose.foundation.layout.Box
@@ -60,19 +62,21 @@
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
-import androidx.compose.runtime.saveable.rememberSaveable
 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.draw.scale
 import androidx.compose.ui.graphics.asImageBitmap
 import androidx.compose.ui.graphics.graphicsLayer
 import androidx.compose.ui.input.key.onKeyEvent
+import androidx.compose.ui.input.pointer.PointerInputChange
 import androidx.compose.ui.input.pointer.pointerInput
 import androidx.compose.ui.platform.LocalContext
 import androidx.compose.ui.platform.LocalDensity
 import androidx.compose.ui.platform.LocalLayoutDirection
 import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.res.stringResource
 import androidx.compose.ui.semantics.Role
 import androidx.compose.ui.semantics.role
 import androidx.compose.ui.semantics.semantics
@@ -122,8 +126,8 @@
     dialogFactory: BouncerDialogFactory,
     modifier: Modifier = Modifier,
 ) {
-    val isSideBySideSupported by viewModel.isSideBySideSupported.collectAsStateWithLifecycle()
-    val layout = calculateLayout(isSideBySideSupported = isSideBySideSupported)
+    val isOneHandedModeSupported by viewModel.isOneHandedModeSupported.collectAsStateWithLifecycle()
+    val layout = calculateLayout(isOneHandedModeSupported = isOneHandedModeSupported)
 
     BouncerContent(layout, viewModel, dialogFactory, modifier)
 }
@@ -136,6 +140,7 @@
     dialogFactory: BouncerDialogFactory,
     modifier: Modifier,
 ) {
+    val scale by viewModel.scale.collectAsStateWithLifecycle()
     Box(
         // Allows the content within each of the layouts to react to the appearance and
         // disappearance of the IME, which is also known as the software keyboard.
@@ -143,7 +148,7 @@
         // Despite the keyboard only being part of the password bouncer, adding it at this level is
         // both necessary to properly handle the keyboard in all layouts and harmless in cases when
         // the keyboard isn't used (like the PIN or pattern auth methods).
-        modifier = modifier.imePadding().onKeyEvent(viewModel::onKeyEvent)
+        modifier = modifier.imePadding().onKeyEvent(viewModel::onKeyEvent).scale(scale)
     ) {
         when (layout) {
             BouncerSceneLayout.STANDARD_BOUNCER -> StandardLayout(viewModel = viewModel)
@@ -299,28 +304,54 @@
     viewModel: BouncerSceneContentViewModel,
     modifier: Modifier = Modifier,
 ) {
-    val layoutDirection = LocalLayoutDirection.current
-    val isLeftToRight = layoutDirection == LayoutDirection.Ltr
-    val (isSwapped, setSwapped) = rememberSaveable(isLeftToRight) { mutableStateOf(!isLeftToRight) }
+    val isLeftToRight = LocalLayoutDirection.current == LayoutDirection.Ltr
+    val isInputPreferredOnLeftSide by
+        viewModel.isInputPreferredOnLeftSide.collectAsStateWithLifecycle()
+    // Swaps the order of user switcher and bouncer input area
+    // Default layout is assumed as user switcher followed by bouncer input area in the direction
+    // of layout.
+    val isSwapped = isLeftToRight == isInputPreferredOnLeftSide
     val isHeightExpanded =
         LocalWindowSizeClass.current.heightSizeClass == WindowHeightSizeClass.Expanded
     val authMethod by viewModel.authMethodViewModel.collectAsStateWithLifecycle()
 
     var swapAnimationEnd by remember { mutableStateOf(false) }
 
+    fun wasEventOnNonInputHalfOfScreen(x: Float, totalWidth: Int): Boolean {
+        // Default layout is assumed as user switcher followed by bouncer input area in
+        // the direction of layout. Swapped layout means that bouncer input area is first, followed
+        // by user switcher in the direction of layout.
+        val halfWidth = totalWidth / 2
+        return if (x > halfWidth) {
+            isLeftToRight && isSwapped
+        } else {
+            isLeftToRight && !isSwapped
+        }
+    }
+
     Row(
         modifier =
             modifier
-                .pointerInput(Unit) {
+                .pointerInput(isSwapped, isInputPreferredOnLeftSide) {
                     detectTapGestures(
                         onDoubleTap = { offset ->
                             // Depending on where the user double tapped, switch the elements such
                             // that the non-swapped element is closer to the side that was double
                             // tapped.
-                            setSwapped(offset.x < size.width / 2)
+                            viewModel.onDoubleTap(
+                                wasEventOnNonInputHalfOfScreen(offset.x, size.width)
+                            )
                         }
                     )
                 }
+                .pointerInput(isSwapped, isInputPreferredOnLeftSide) {
+                    awaitEachGesture {
+                        val downEvent: PointerInputChange = awaitFirstDown()
+                        viewModel.onDown(
+                            wasEventOnNonInputHalfOfScreen(downEvent.position.x, size.width)
+                        )
+                    }
+                }
                 .testTag("BesideUserSwitcherLayout")
                 .motionTestValues {
                     swapAnimationEnd exportAs BouncerMotionTestKeys.swapAnimationEnd
@@ -639,7 +670,7 @@
     val appearMoveAnimatable = remember { Animatable(0f) }
     val appearAnimationInitialOffset = with(LocalDensity.current) { 80.dp.toPx() }
 
-    actionButton?.let { actionButtonViewModel ->
+    actionButton?.let { actionButtonModel ->
         LaunchedEffect(Unit) {
             appearFadeInAnimatable.animateTo(
                 targetValue = 1f,
@@ -678,12 +709,14 @@
                     .background(color = MaterialTheme.colorScheme.tertiaryContainer)
                     .semantics { role = Role.Button }
                     .combinedClickable(
-                        onClick = { actionButtonViewModel.onClick() },
-                        onLongClick = actionButtonViewModel.onLongClick?.let { { it.invoke() } },
+                        onClick = { actionButton?.let { viewModel.onActionButtonClicked(it) } },
+                        onLongClick = {
+                            actionButton?.let { viewModel.onActionButtonLongClicked(it) }
+                        },
                     )
         ) {
             Text(
-                text = actionButtonViewModel.label,
+                text = stringResource(id = actionButtonModel.labelResId),
                 style = MaterialTheme.typography.bodyMedium,
                 color = MaterialTheme.colorScheme.onTertiaryContainer,
                 modifier = Modifier.align(Alignment.Center).padding(ButtonDefaults.ContentPadding),
@@ -723,7 +756,8 @@
 /** Renders the UI of the user switcher that's displayed on large screens next to the bouncer UI. */
 @Composable
 private fun UserSwitcher(viewModel: BouncerSceneContentViewModel, modifier: Modifier = Modifier) {
-    if (!viewModel.isUserSwitcherVisible) {
+    val isUserSwitcherVisible by viewModel.isUserSwitcherVisible.collectAsStateWithLifecycle()
+    if (!isUserSwitcherVisible) {
         // Take up the same space as the user switcher normally would, but with nothing inside it.
         Box(modifier = modifier)
         return
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerSceneLayout.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerSceneLayout.kt
index 1c3d93c..eb62d33 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerSceneLayout.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerSceneLayout.kt
@@ -26,19 +26,17 @@
 
 /**
  * Returns the [BouncerSceneLayout] that should be used by the bouncer scene. If
- * [isSideBySideSupported] is `false`, then [BouncerSceneLayout.BESIDE_USER_SWITCHER] is replaced by
- * [BouncerSceneLayout.STANDARD_BOUNCER].
+ * [isOneHandedModeSupported] is `false`, then [BouncerSceneLayout.BESIDE_USER_SWITCHER] is replaced
+ * by [BouncerSceneLayout.STANDARD_BOUNCER].
  */
 @Composable
-fun calculateLayout(
-    isSideBySideSupported: Boolean,
-): BouncerSceneLayout {
+fun calculateLayout(isOneHandedModeSupported: Boolean): BouncerSceneLayout {
     val windowSizeClass = LocalWindowSizeClass.current
 
     return calculateLayoutInternal(
         width = windowSizeClass.widthSizeClass.toEnum(),
         height = windowSizeClass.heightSizeClass.toEnum(),
-        isSideBySideSupported = isSideBySideSupported,
+        isOneHandedModeSupported = isOneHandedModeSupported,
     )
 }
 
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 476cced..5e1ac1f 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
@@ -181,9 +181,11 @@
 import com.android.systemui.communal.ui.viewmodel.CommunalEditModeViewModel
 import com.android.systemui.communal.ui.viewmodel.CommunalViewModel
 import com.android.systemui.communal.ui.viewmodel.ResizeInfo
+import com.android.systemui.communal.ui.viewmodel.ResizeableItemFrameViewModel
 import com.android.systemui.communal.util.DensityUtils.Companion.adjustedDp
 import com.android.systemui.communal.widgets.SmartspaceAppWidgetHostView
 import com.android.systemui.communal.widgets.WidgetConfigurator
+import com.android.systemui.lifecycle.rememberViewModel
 import com.android.systemui.res.R
 import com.android.systemui.statusbar.phone.SystemUIDialogFactory
 import kotlin.math.max
@@ -665,6 +667,7 @@
     maxHeightPx: Int,
     modifier: Modifier = Modifier,
     alpha: () -> Float = { 1f },
+    viewModel: ResizeableItemFrameViewModel,
     onResize: (info: ResizeInfo) -> Unit = {},
     content: @Composable (modifier: Modifier) -> Unit,
 ) {
@@ -680,6 +683,7 @@
             enabled = enabled,
             alpha = alpha,
             modifier = modifier,
+            viewModel = viewModel,
             onResize = onResize,
             minHeightPx = minHeightPx,
             maxHeightPx = maxHeightPx,
@@ -711,7 +715,7 @@
             WidgetSizeInfo(minHeightPx, maxHeightPx)
         }
     } else {
-        WidgetSizeInfo(0, Int.MAX_VALUE)
+        WidgetSizeInfo(0, 0)
     }
 }
 
@@ -796,6 +800,14 @@
                     false
                 }
 
+            val resizeableItemFrameViewModel =
+                rememberViewModel(
+                    key = item.size.span,
+                    traceName = "ResizeableItemFrame.viewModel.$index",
+                ) {
+                    ResizeableItemFrameViewModel()
+                }
+
             if (viewModel.isEditMode && dragDropState != null) {
                 val isItemDragging = dragDropState.draggingItemKey == item.key
                 val outlineAlpha by
@@ -821,6 +833,7 @@
                                 )
                             }
                             .thenIf(isItemDragging) { Modifier.zIndex(1f) },
+                    viewModel = resizeableItemFrameViewModel,
                     onResize = { resizeInfo -> contentListState.resize(index, resizeInfo) },
                     minHeightPx = widgetSizeInfo.minHeightPx,
                     maxHeightPx = widgetSizeInfo.maxHeightPx,
@@ -843,6 +856,7 @@
                             contentListState = contentListState,
                             interactionHandler = interactionHandler,
                             widgetSection = widgetSection,
+                            resizeableItemFrameViewModel = resizeableItemFrameViewModel,
                         )
                     }
                 }
@@ -857,6 +871,7 @@
                     contentListState = contentListState,
                     interactionHandler = interactionHandler,
                     widgetSection = widgetSection,
+                    resizeableItemFrameViewModel = resizeableItemFrameViewModel,
                 )
             }
         }
@@ -1080,6 +1095,7 @@
     contentListState: ContentListState,
     interactionHandler: RemoteViews.InteractionHandler?,
     widgetSection: CommunalAppWidgetSection,
+    resizeableItemFrameViewModel: ResizeableItemFrameViewModel,
 ) {
     when (model) {
         is CommunalContentModel.WidgetContent.Widget ->
@@ -1093,6 +1109,7 @@
                 index,
                 contentListState,
                 widgetSection,
+                resizeableItemFrameViewModel,
             )
         is CommunalContentModel.WidgetPlaceholder -> HighlightedItem(modifier)
         is CommunalContentModel.WidgetContent.DisabledWidget ->
@@ -1223,7 +1240,9 @@
     index: Int,
     contentListState: ContentListState,
     widgetSection: CommunalAppWidgetSection,
+    resizeableItemFrameViewModel: ResizeableItemFrameViewModel,
 ) {
+    val coroutineScope = rememberCoroutineScope()
     val context = LocalContext.current
     val accessibilityLabel =
         remember(model, context) {
@@ -1234,6 +1253,10 @@
     val placeWidgetActionLabel = stringResource(R.string.accessibility_action_label_place_widget)
     val unselectWidgetActionLabel =
         stringResource(R.string.accessibility_action_label_unselect_widget)
+
+    val shrinkWidgetLabel = stringResource(R.string.accessibility_action_label_shrink_widget)
+    val expandWidgetLabel = stringResource(R.string.accessibility_action_label_expand_widget)
+
     val selectedKey by viewModel.selectedKey.collectAsStateWithLifecycle()
     val selectedIndex =
         selectedKey?.let { key -> contentListState.list.indexOfFirst { it.key == key } }
@@ -1292,6 +1315,29 @@
                                 true
                             }
                         val actions = mutableListOf(deleteAction)
+
+                        if (communalWidgetResizing() && resizeableItemFrameViewModel.canShrink()) {
+                            actions.add(
+                                CustomAccessibilityAction(shrinkWidgetLabel) {
+                                    coroutineScope.launch {
+                                        resizeableItemFrameViewModel.shrinkToNextAnchor()
+                                    }
+                                    true
+                                }
+                            )
+                        }
+
+                        if (communalWidgetResizing() && resizeableItemFrameViewModel.canExpand()) {
+                            actions.add(
+                                CustomAccessibilityAction(expandWidgetLabel) {
+                                    coroutineScope.launch {
+                                        resizeableItemFrameViewModel.expandToNextAnchor()
+                                    }
+                                    true
+                                }
+                            )
+                        }
+
                         if (selectedIndex != null && selectedIndex != index) {
                             actions.add(
                                 CustomAccessibilityAction(placeWidgetActionLabel) {
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/ResizeableItemFrame.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/ResizeableItemFrame.kt
index 521330f..8e85432 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/ResizeableItemFrame.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/ResizeableItemFrame.kt
@@ -56,7 +56,6 @@
 import com.android.systemui.communal.ui.viewmodel.DragHandle
 import com.android.systemui.communal.ui.viewmodel.ResizeInfo
 import com.android.systemui.communal.ui.viewmodel.ResizeableItemFrameViewModel
-import com.android.systemui.lifecycle.rememberViewModel
 import kotlinx.coroutines.flow.collectLatest
 import kotlinx.coroutines.flow.combine
 
@@ -192,16 +191,12 @@
     maxHeightPx: Int = Int.MAX_VALUE,
     resizeMultiple: Int = 1,
     alpha: () -> Float = { 1f },
+    viewModel: ResizeableItemFrameViewModel,
     onResize: (info: ResizeInfo) -> Unit = {},
     content: @Composable () -> Unit,
 ) {
     val brush = SolidColor(outlineColor)
     val onResizeUpdated by rememberUpdatedState(onResize)
-    val viewModel =
-        rememberViewModel(key = currentSpan, traceName = "ResizeableItemFrame.viewModel") {
-            ResizeableItemFrameViewModel()
-        }
-
     val dragHandleHeight = verticalArrangement.spacing - outlinePadding * 2
     val isDragging by
         remember(viewModel) {
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/StatusBarSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/StatusBarSection.kt
index f9e2252..0d8a470 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/StatusBarSection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/StatusBarSection.kt
@@ -21,12 +21,10 @@
 import android.view.View
 import android.view.ViewGroup
 import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.foundation.layout.padding
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.remember
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.platform.LocalContext
-import androidx.compose.ui.unit.dp
 import androidx.compose.ui.viewinterop.AndroidView
 import com.android.compose.animation.scene.SceneScope
 import com.android.compose.modifiers.height
@@ -53,17 +51,13 @@
         @SuppressLint("InflateParams")
         val view =
             remember(context) {
-                (LayoutInflater.from(context)
-                        .inflate(
-                            R.layout.keyguard_status_bar,
-                            null,
-                            false,
-                        ) as KeyguardStatusBarView)
+                (LayoutInflater.from(context).inflate(R.layout.keyguard_status_bar, null, false)
+                        as KeyguardStatusBarView)
                     .also {
                         it.layoutParams =
                             ViewGroup.LayoutParams(
                                 ViewGroup.LayoutParams.MATCH_PARENT,
-                                ViewGroup.LayoutParams.WRAP_CONTENT
+                                ViewGroup.LayoutParams.WRAP_CONTENT,
                             )
                     }
             }
@@ -92,10 +86,8 @@
                 view
             },
             modifier =
-                Modifier.fillMaxWidth().padding(horizontal = 16.dp).height {
-                    Utils.getStatusBarHeaderHeightKeyguard(context)
-                },
-            update = { viewController.setDisplayCutout(viewDisplayCutout) }
+                modifier.fillMaxWidth().height { Utils.getStatusBarHeaderHeightKeyguard(context) },
+            update = { viewController.setDisplayCutout(viewDisplayCutout) },
         )
     }
 }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationLockscreenScrim.kt b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationLockscreenScrim.kt
index 4279be3..48067ce 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationLockscreenScrim.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationLockscreenScrim.kt
@@ -21,7 +21,6 @@
 import androidx.compose.foundation.background
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.fillMaxSize
-import androidx.compose.material3.MaterialTheme
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.LaunchedEffect
 import androidx.compose.runtime.remember
@@ -99,12 +98,18 @@
         }
     }
 
+    val isBouncerToLockscreen =
+        layoutState.currentTransition?.isTransitioning(
+            from = Scenes.Bouncer,
+            to = Scenes.Lockscreen,
+        ) ?: false
+
     Box(
         modifier
             .fillMaxSize()
-            .element(Notifications.Elements.NotificationScrim)
+            .element(viewModel.element.key)
             .graphicsLayer { alpha = alphaAnimatable.value }
-            .background(MaterialTheme.colorScheme.surface)
+            .background(viewModel.element.color(isBouncerToLockscreen))
     )
 }
 
@@ -112,7 +117,7 @@
     currentTransition: TransitionState.Transition,
     shadeMode: ShadeMode,
 ): Boolean {
-    return shadeMode == ShadeMode.Single &&
+    return shadeMode != ShadeMode.Dual &&
         currentTransition.isInitiatedByUserInput &&
         (currentTransition.isTransitioning(from = Scenes.Shade, to = Scenes.Lockscreen) ||
             currentTransition.isTransitioning(from = Scenes.Bouncer, to = Scenes.Lockscreen))
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationScrimNestedScrollConnection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationScrimNestedScrollConnection.kt
index e4c60e1..5cb45e5 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationScrimNestedScrollConnection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationScrimNestedScrollConnection.kt
@@ -18,9 +18,11 @@
 
 import androidx.compose.foundation.gestures.Orientation
 import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
+import androidx.compose.ui.input.nestedscroll.NestedScrollSource
 import androidx.compose.ui.util.fastCoerceAtLeast
 import androidx.compose.ui.util.fastCoerceAtMost
 import com.android.compose.nestedscroll.PriorityNestedScrollConnection
+import com.android.compose.nestedscroll.ScrollController
 
 /**
  * A [NestedScrollConnection] that listens for all vertical scroll events and responds in the
@@ -58,33 +60,40 @@
             offsetAvailable > 0 && (scrimOffset() < maxScrimOffset || isCurrentGestureOverscroll())
         },
         canStartPostFling = { false },
-        canStopOnPreFling = { false },
-        onStart = { offsetAvailable -> onStart(offsetAvailable) },
-        onScroll = { offsetAvailable, _ ->
-            val currentHeight = scrimOffset()
-            val amountConsumed =
-                if (offsetAvailable > 0) {
-                    val amountLeft = maxScrimOffset - currentHeight
-                    offsetAvailable.fastCoerceAtMost(amountLeft)
-                } else {
-                    val amountLeft = minScrimOffset() - currentHeight
-                    offsetAvailable.fastCoerceAtLeast(amountLeft)
+        onStart = { firstScroll ->
+            onStart(firstScroll)
+            object : ScrollController {
+                override fun onScroll(deltaScroll: Float, source: NestedScrollSource): Float {
+                    val currentHeight = scrimOffset()
+                    val amountConsumed =
+                        if (deltaScroll > 0) {
+                            val amountLeft = maxScrimOffset - currentHeight
+                            deltaScroll.fastCoerceAtMost(amountLeft)
+                        } else {
+                            val amountLeft = minScrimOffset() - currentHeight
+                            deltaScroll.fastCoerceAtLeast(amountLeft)
+                        }
+                    snapScrimOffset(currentHeight + amountConsumed)
+                    return amountConsumed
                 }
-            snapScrimOffset(currentHeight + amountConsumed)
-            amountConsumed
-        },
-        onStop = { velocityAvailable ->
-            onStop(velocityAvailable)
-            if (scrimOffset() < minScrimOffset()) {
-                animateScrimOffset(minScrimOffset())
-            }
-            // Don't consume the velocity on pre/post fling
-            0f
-        },
-        onCancel = {
-            onStop(0f)
-            if (scrimOffset() < minScrimOffset()) {
-                animateScrimOffset(minScrimOffset())
+
+                override suspend fun onStop(initialVelocity: Float): Float {
+                    onStop(initialVelocity)
+                    if (scrimOffset() < minScrimOffset()) {
+                        animateScrimOffset(minScrimOffset())
+                    }
+                    // Don't consume the velocity on pre/post fling
+                    return 0f
+                }
+
+                override fun onCancel() {
+                    onStop(0f)
+                    if (scrimOffset() < minScrimOffset()) {
+                        animateScrimOffset(minScrimOffset())
+                    }
+                }
+
+                override fun canStopOnPreFling() = false
             }
         },
     )
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationStackNestedScrollConnection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationStackNestedScrollConnection.kt
index edb05eb..e1b74a9 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationStackNestedScrollConnection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationStackNestedScrollConnection.kt
@@ -23,6 +23,7 @@
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.remember
 import androidx.compose.ui.Modifier
+import androidx.compose.ui.input.nestedscroll.NestedScrollSource
 import androidx.compose.ui.input.nestedscroll.nestedScroll
 import androidx.compose.ui.platform.LocalConfiguration
 import androidx.compose.ui.platform.LocalDensity
@@ -30,6 +31,7 @@
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.util.fastCoerceAtLeast
 import com.android.compose.nestedscroll.PriorityNestedScrollConnection
+import com.android.compose.nestedscroll.ScrollController
 import kotlin.math.max
 import kotlin.math.roundToInt
 import kotlin.math.tanh
@@ -92,20 +94,29 @@
             offsetAvailable < 0f && offsetBeforeStart < 0f && !canScrollForward()
         },
         canStartPostFling = { velocityAvailable -> velocityAvailable < 0f && !canScrollForward() },
-        canStopOnPreFling = { false },
-        onStart = { offsetAvailable -> onStart(offsetAvailable) },
-        onScroll = { offsetAvailable, _ ->
-            val minOffset = 0f
-            val consumed = offsetAvailable.fastCoerceAtLeast(minOffset - stackOffset())
-            if (consumed != 0f) {
-                onScroll(consumed)
+        onStart = { firstScroll ->
+            onStart(firstScroll)
+            object : ScrollController {
+                override fun onScroll(deltaScroll: Float, source: NestedScrollSource): Float {
+                    val minOffset = 0f
+                    val consumed = deltaScroll.fastCoerceAtLeast(minOffset - stackOffset())
+                    if (consumed != 0f) {
+                        onScroll(consumed)
+                    }
+                    return consumed
+                }
+
+                override suspend fun onStop(initialVelocity: Float): Float {
+                    onStop(initialVelocity)
+                    return initialVelocity
+                }
+
+                override fun onCancel() {
+                    onStop(0f)
+                }
+
+                override fun canStopOnPreFling() = false
             }
-            consumed
         },
-        onStop = { velocityAvailable ->
-            onStop(velocityAvailable)
-            velocityAvailable
-        },
-        onCancel = { onStop(0f) },
     )
 }
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 834a7f5..4fa1984 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
@@ -375,9 +375,13 @@
     LaunchedEffect(shadeScrollState) { viewModel.setScrollState(shadeScrollState) }
 
     // if contentHeight drops below minimum visible scrim height while scrim is
-    // expanded, reset scrim offset.
-    LaunchedEffect(stackHeight, scrimOffset) {
-        snapshotFlow { stackHeight.intValue < minVisibleScrimHeight() && scrimOffset.value < 0f }
+    // expanded and IME is not showing, reset scrim offset.
+    LaunchedEffect(stackHeight, scrimOffset, imeTop) {
+        snapshotFlow {
+                stackHeight.intValue < minVisibleScrimHeight() &&
+                    scrimOffset.value < 0f &&
+                    imeTop.floatValue <= 0f
+            }
             .collect { shouldCollapse -> if (shouldCollapse) scrimOffset.animateTo(0f, tween()) }
     }
 
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 58801e0..e725ce5 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
@@ -128,10 +128,10 @@
                         QSSceneAdapter.State.QS
                     }
                     else ->
-                        error(
-                            "Bad transition for QuickSettings: fromContent=$fromContent," +
-                                " toScene=$toContent"
-                        )
+                        // We are not in a transition between states that have QS, so just make
+                        // sure it's closed. This could be an issue if going from SplitShade to
+                        // a folded device.
+                        QSSceneAdapter.State.CLOSED
                 }
             }
         is TransitionState.Transition.OverlayTransition ->
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 dc545b8..67f412e 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
@@ -80,6 +80,7 @@
     from(Scenes.Lockscreen, to = Scenes.Shade) { lockscreenToShadeTransition() }
     from(Scenes.Lockscreen, to = Scenes.Shade, key = ToSplitShade) {
         lockscreenToSplitShadeTransition()
+        sharedElement(Shade.Elements.BackgroundScrim, enabled = false)
     }
     from(Scenes.Lockscreen, to = Scenes.Shade, key = SlightlyFasterShadeCollapse) {
         lockscreenToShadeTransition(durationScale = 0.9)
@@ -96,6 +97,9 @@
         sharedElement(Notifications.Elements.NotificationStackPlaceholder, enabled = false)
         sharedElement(Notifications.Elements.HeadsUpNotificationPlaceholder, enabled = false)
     }
+    from(Scenes.Shade, to = Scenes.Lockscreen, key = ToSplitShade) {
+        reversed { lockscreenToSplitShadeTransition() }
+    }
     from(Scenes.Communal, to = Scenes.Shade) { communalToShadeTransition() }
     from(Scenes.Communal, to = Scenes.Bouncer) { communalToBouncerTransition() }
 
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 491221f..bba3d69 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
@@ -516,13 +516,14 @@
                             .weight(1f)
                             .graphicsLayer { translationX = unfoldTranslationXForStartSide }
                 ) {
-                    BrightnessMirror(
-                        viewModel = brightnessMirrorViewModel,
-                        qsSceneAdapter = viewModel.qsSceneAdapter,
-                        // Need to use the offset measured from the container as the header
-                        // has to be accounted for
-                        measureFromContainer = true,
-                    )
+                    Box(modifier = Modifier.fillMaxSize()) {
+                        BrightnessMirror(
+                            viewModel = brightnessMirrorViewModel,
+                            qsSceneAdapter = viewModel.qsSceneAdapter,
+                            modifier = Modifier.align(Alignment.TopCenter),
+                            measureFromContainer = true,
+                        )
+                    }
                     Column(
                         verticalArrangement = Arrangement.Top,
                         modifier = Modifier.fillMaxSize().padding(bottom = bottomPadding),
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 d4f3b5b..28a12f8 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
@@ -78,9 +78,7 @@
 ) {
     require(viewModels.isNotEmpty())
     Column(modifier = modifier) {
-        Box(
-            modifier = Modifier.fillMaxWidth(),
-        ) {
+        Box(modifier = Modifier.fillMaxWidth()) {
             val sliderViewModel: SliderViewModel = viewModels.first()
             val sliderState by viewModels.first().slider.collectAsStateWithLifecycle()
             val sliderPadding by topSliderPadding(isExpandable)
@@ -94,6 +92,7 @@
                 onValueChangeFinished = { sliderViewModel.onValueChangeFinished() },
                 onIconTapped = { sliderViewModel.toggleMuted(sliderState) },
                 sliderColors = sliderColors,
+                hapticsViewModelFactory = sliderViewModel.hapticsViewModelFactory,
             )
 
             ExpandButton(
@@ -143,6 +142,7 @@
                             onValueChangeFinished = { sliderViewModel.onValueChangeFinished() },
                             onIconTapped = { sliderViewModel.toggleMuted(sliderState) },
                             sliderColors = sliderColors,
+                            hapticsViewModelFactory = sliderViewModel.hapticsViewModelFactory,
                         )
                     }
                 }
@@ -181,7 +181,7 @@
             colors =
                 IconButtonDefaults.filledIconButtonColors(
                     containerColor = sliderColors.indicatorColor,
-                    contentColor = sliderColors.iconColor
+                    contentColor = sliderColors.iconColor,
                 ),
         ) {
             Icon(
@@ -211,9 +211,7 @@
             animationSpec = tween(durationMillis = enterDuration, delayMillis = enterDelay),
             clip = false,
         ) +
-        fadeIn(
-            animationSpec = tween(durationMillis = enterDuration, delayMillis = enterDelay),
-        )
+        fadeIn(animationSpec = tween(durationMillis = enterDuration, delayMillis = enterDelay))
 }
 
 private fun exitTransition(index: Int, totalCount: Int): ExitTransition {
@@ -286,6 +284,6 @@
                 0.dp
             },
         animationSpec = animationSpec,
-        label = "TopVolumeSliderPadding"
+        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 d15430f..a0e46d5 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
@@ -49,6 +49,7 @@
                 onValueChangeFinished = { sliderViewModel.onValueChangeFinished() },
                 onIconTapped = { sliderViewModel.toggleMuted(sliderState) },
                 sliderColors = sliderColors,
+                hapticsViewModelFactory = sliderViewModel.hapticsViewModelFactory,
             )
         }
     }
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 a23bb67..eb79b90 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
@@ -22,6 +22,8 @@
 import androidx.compose.animation.fadeIn
 import androidx.compose.animation.fadeOut
 import androidx.compose.foundation.clickable
+import androidx.compose.foundation.gestures.Orientation
+import androidx.compose.foundation.interaction.MutableInteractionSource
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.foundation.layout.size
@@ -46,9 +48,14 @@
 import androidx.compose.ui.unit.dp
 import com.android.compose.PlatformSlider
 import com.android.compose.PlatformSliderColors
+import com.android.systemui.Flags
 import com.android.systemui.common.shared.model.Icon
 import com.android.systemui.common.ui.compose.Icon
 import com.android.systemui.compose.modifiers.sysuiResTag
+import com.android.systemui.haptics.slider.SeekableSliderTrackerConfig
+import com.android.systemui.haptics.slider.SliderHapticFeedbackConfig
+import com.android.systemui.haptics.slider.compose.ui.SliderHapticsViewModel
+import com.android.systemui.lifecycle.rememberViewModel
 import com.android.systemui.volume.panel.component.volume.slider.ui.viewmodel.SliderState
 
 @Composable
@@ -59,8 +66,40 @@
     onIconTapped: () -> Unit,
     modifier: Modifier = Modifier,
     sliderColors: PlatformSliderColors,
+    hapticsViewModelFactory: SliderHapticsViewModel.Factory,
 ) {
     val value by valueState(state)
+    val interactionSource = remember { MutableInteractionSource() }
+    val sliderStepSize = 1f / (state.valueRange.endInclusive - state.valueRange.start)
+    val hapticsViewModel: SliderHapticsViewModel? =
+        if (Flags.hapticsForComposeSliders()) {
+            rememberViewModel(traceName = "SliderHapticsViewModel") {
+                hapticsViewModelFactory.create(
+                    interactionSource,
+                    state.valueRange,
+                    Orientation.Horizontal,
+                    SliderHapticFeedbackConfig(
+                        lowerBookendScale = 0.2f,
+                        progressBasedDragMinScale = 0.2f,
+                        progressBasedDragMaxScale = 0.5f,
+                        deltaProgressForDragThreshold = 0f,
+                        additionalVelocityMaxBump = 0.2f,
+                        maxVelocityToScale = 0.1f, /* slider progress(from 0 to 1) per sec */
+                        sliderStepSize = sliderStepSize,
+                    ),
+                    SeekableSliderTrackerConfig(
+                        lowerBookendThreshold = 0f,
+                        upperBookendThreshold = 1f,
+                    ),
+                )
+            }
+        } else {
+            null
+        }
+
+    // Perform haptics due to UI composition
+    hapticsViewModel?.onValueChange(value)
+
     PlatformSlider(
         modifier =
             modifier.sysuiResTag(state.label).clearAndSetSemantics {
@@ -94,7 +133,7 @@
                     val newValue =
                         (value + targetDirection * state.a11yStep).coerceIn(
                             state.valueRange.start,
-                            state.valueRange.endInclusive
+                            state.valueRange.endInclusive,
                         )
                     onValueChange(newValue)
                     true
@@ -102,16 +141,18 @@
             },
         value = value,
         valueRange = state.valueRange,
-        onValueChange = onValueChange,
-        onValueChangeFinished = onValueChangeFinished,
+        onValueChange = { newValue ->
+            hapticsViewModel?.addVelocityDataPoint(newValue)
+            onValueChange(newValue)
+        },
+        onValueChangeFinished = {
+            hapticsViewModel?.onValueChangeEnded()
+            onValueChangeFinished?.invoke()
+        },
         enabled = state.isEnabled,
         icon = {
             state.icon?.let {
-                SliderIcon(
-                    icon = it,
-                    onIconTapped = onIconTapped,
-                    isTappable = state.isMutable,
-                )
+                SliderIcon(icon = it, onIconTapped = onIconTapped, isTappable = state.isMutable)
             }
         },
         colors = sliderColors,
@@ -128,7 +169,8 @@
                     disabledMessage = state.disabledMessage,
                 )
             }
-        }
+        },
+        interactionSource = interactionSource,
     )
 }
 
@@ -150,14 +192,14 @@
     icon: Icon,
     onIconTapped: () -> Unit,
     isTappable: Boolean,
-    modifier: Modifier = Modifier
+    modifier: Modifier = Modifier,
 ) {
     val boxModifier =
         if (isTappable) {
                 modifier.clickable(
                     onClick = onIconTapped,
                     interactionSource = null,
-                    indication = null
+                    indication = null,
                 )
             } else {
                 modifier
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt
index 8469007..7c7202a 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt
@@ -20,6 +20,7 @@
 
 import androidx.compose.foundation.gestures.Orientation
 import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.input.nestedscroll.NestedScrollSource
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.unit.round
 import androidx.compose.ui.util.fastCoerceIn
@@ -27,6 +28,7 @@
 import com.android.compose.animation.scene.content.state.TransitionState
 import com.android.compose.animation.scene.content.state.TransitionState.HasOverscrollProperties.Companion.DistanceUnspecified
 import com.android.compose.nestedscroll.PriorityNestedScrollConnection
+import com.android.compose.nestedscroll.ScrollController
 import kotlin.math.absoluteValue
 
 internal typealias SuspendedValue<T> = suspend () -> T
@@ -66,6 +68,7 @@
     internal val orientation: Orientation,
 ) : DraggableHandler {
     internal val nestedScrollKey = Any()
+
     /** The [DraggableHandler] can only have one active [DragController] at a time. */
     private var dragController: DragControllerImpl? = null
 
@@ -345,6 +348,7 @@
                     distance == DistanceUnspecified ||
                         swipeAnimation.contentTransition.isWithinProgressRange(desiredProgress) ->
                         desiredOffset
+
                     distance > 0f -> desiredOffset.fastCoerceIn(0f, distance)
                     else -> desiredOffset.fastCoerceIn(distance, 0f)
                 }
@@ -545,6 +549,7 @@
             upOrLeftResult == null && downOrRightResult == null -> null
             (directionOffset < 0f && upOrLeftResult != null) || downOrRightResult == null ->
                 upOrLeftResult
+
             else -> downOrRightResult
         }
     }
@@ -608,7 +613,6 @@
             return overscrollSpec != null
         }
 
-        var dragController: DragController? = null
         var isIntercepting = false
 
         return PriorityNestedScrollConnection(
@@ -669,10 +673,12 @@
                             canChangeScene = isZeroOffset
                             isZeroOffset && hasNextScene(offsetAvailable)
                         }
+
                         NestedScrollBehavior.EdgeWithPreview -> {
                             canChangeScene = isZeroOffset
                             hasNextScene(offsetAvailable)
                         }
+
                         NestedScrollBehavior.EdgeAlways -> {
                             canChangeScene = true
                             hasNextScene(offsetAvailable)
@@ -710,56 +716,59 @@
 
                 canStart
             },
-            // We need to maintain scroll priority even if the scene transition can no longer
-            // consume the scroll gesture to allow us to return to the previous scene.
-            canStopOnScroll = { _, _ -> false },
-            canStopOnPreFling = { true },
-            onStart = { offsetAvailable ->
+            onStart = { firstScroll ->
                 val pointersInfo = pointersInfo()
-                dragController =
-                    draggableHandler.onDragStarted(
-                        pointersDown = pointersInfo.pointersDown,
-                        startedPosition = pointersInfo.startedPosition,
-                        overSlop = if (isIntercepting) 0f else offsetAvailable,
-                    )
-            },
-            onScroll = { offsetAvailable, _ ->
-                val controller = dragController ?: error("Should be called after onStart")
-
-                val pointersInfo = pointersInfoOwner.pointersInfo()
-                if (pointersInfo.isMouseWheel) {
-                    // Do not support mouse wheel interactions
-                    return@PriorityNestedScrollConnection 0f
-                }
-
-                // TODO(b/297842071) We should handle the overscroll or slow drag if the gesture is
-                // initiated in a nested child.
-                controller.onDrag(delta = offsetAvailable)
-            },
-            onStop = { velocityAvailable ->
-                val controller = dragController ?: error("Should be called after onStart")
-                try {
-                    controller
-                        .onStop(velocity = velocityAvailable, canChangeContent = canChangeScene)
-                        .invoke()
-                } finally {
-                    // onStop might still be running when a new gesture begins.
-                    // To prevent conflicts, we should only remove the drag controller if it's the
-                    // same one that was active initially.
-                    if (dragController == controller) {
-                        dragController = null
-                    }
-                }
-            },
-            onCancel = {
-                val controller = dragController ?: error("Should be called after onStart")
-                controller.onStop(velocity = 0f, canChangeContent = canChangeScene)
-                dragController = null
+                scrollController(
+                    dragController =
+                        draggableHandler.onDragStarted(
+                            pointersDown = pointersInfo.pointersDown,
+                            startedPosition = pointersInfo.startedPosition,
+                            overSlop = if (isIntercepting) 0f else firstScroll,
+                        ),
+                    canChangeScene = canChangeScene,
+                    pointersInfoOwner = pointersInfoOwner,
+                )
             },
         )
     }
 }
 
+private fun scrollController(
+    dragController: DragController,
+    canChangeScene: Boolean,
+    pointersInfoOwner: PointersInfoOwner,
+): ScrollController {
+    return object : ScrollController {
+        override fun onScroll(deltaScroll: Float, source: NestedScrollSource): Float {
+            val pointersInfo = pointersInfoOwner.pointersInfo()
+            if (pointersInfo.isMouseWheel) {
+                // Do not support mouse wheel interactions
+                return 0f
+            }
+
+            return dragController.onDrag(delta = deltaScroll)
+        }
+
+        override suspend fun onStop(initialVelocity: Float): Float {
+            return dragController
+                .onStop(velocity = initialVelocity, canChangeContent = canChangeScene)
+                .invoke()
+        }
+
+        override fun onCancel() {
+            dragController.onStop(velocity = 0f, canChangeContent = canChangeScene)
+        }
+
+        /**
+         * We need to maintain scroll priority even if the scene transition can no longer consume
+         * the scroll gesture to allow us to return to the previous scene.
+         */
+        override fun canCancelScroll(available: Float, consumed: Float) = false
+
+        override fun canStopOnPreFling() = true
+    }
+}
+
 /**
  * The number of pixels below which there won't be a visible difference in the transition and from
  * which the animation can stop.
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/NestedScrollToScene.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/NestedScrollToScene.kt
index 8a0e462..fbd1cd5 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/NestedScrollToScene.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/NestedScrollToScene.kt
@@ -128,10 +128,10 @@
 ) : DelegatingNode() {
     private var scrollBehaviorOwner: ScrollBehaviorOwner? = null
 
-    private fun requireScrollBehaviorOwner(): ScrollBehaviorOwner {
+    private fun findScrollBehaviorOwner(): ScrollBehaviorOwner? {
         var behaviorOwner = scrollBehaviorOwner
         if (behaviorOwner == null) {
-            behaviorOwner = requireScrollBehaviorOwner(layoutImpl.draggableHandler(orientation))
+            behaviorOwner = findScrollBehaviorOwner(layoutImpl.draggableHandler(orientation))
             scrollBehaviorOwner = behaviorOwner
         }
         return behaviorOwner
@@ -156,8 +156,8 @@
                 // transition between scenes. We can assume that the behavior is only needed if
                 // there is some remaining amount.
                 if (available != Offset.Zero) {
-                    requireScrollBehaviorOwner()
-                        .updateScrollBehaviors(
+                    findScrollBehaviorOwner()
+                        ?.updateScrollBehaviors(
                             topOrLeftBehavior = topOrLeftBehavior,
                             bottomOrRightBehavior = bottomOrRightBehavior,
                             isExternalOverscrollGesture = isExternalOverscrollGesture,
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt
index a3f2a43..fdf01cc 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt
@@ -172,15 +172,12 @@
 }
 
 /** Find the [ScrollBehaviorOwner] for the current orientation. */
-internal fun DelegatableNode.requireScrollBehaviorOwner(
+internal fun DelegatableNode.findScrollBehaviorOwner(
     draggableHandler: DraggableHandlerImpl
-): ScrollBehaviorOwner {
-    val ancestorNode =
-        checkNotNull(findNearestAncestor(draggableHandler.nestedScrollKey)) {
-            "This should never happen! Couldn't find a ScrollBehaviorOwner. " +
-                "Are we inside an SceneTransitionLayout?"
-        }
-    return ancestorNode as ScrollBehaviorOwner
+): ScrollBehaviorOwner? {
+    // If there are no scenes in a particular orientation, the corresponding ScrollBehaviorOwnerNode
+    // is removed from the composition.
+    return findNearestAncestor(draggableHandler.nestedScrollKey) as? ScrollBehaviorOwner
 }
 
 internal fun interface ScrollBehaviorOwner {
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/LargeTopAppBarNestedScrollConnection.kt b/packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/LargeTopAppBarNestedScrollConnection.kt
index ecf64b7..255da31 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/LargeTopAppBarNestedScrollConnection.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/LargeTopAppBarNestedScrollConnection.kt
@@ -18,6 +18,7 @@
 
 import androidx.compose.foundation.gestures.Orientation
 import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
+import androidx.compose.ui.input.nestedscroll.NestedScrollSource
 import androidx.compose.ui.util.fastCoerceAtLeast
 import androidx.compose.ui.util.fastCoerceAtMost
 
@@ -54,23 +55,38 @@
             offsetAvailable > 0 && height() < maxHeight()
         },
         canStartPostFling = { false },
-        canStopOnPreFling = { false },
-        onStart = { /* do nothing */ },
-        onScroll = { offsetAvailable, _ ->
-            val currentHeight = height()
-            val amountConsumed =
-                if (offsetAvailable > 0) {
-                    val amountLeft = maxHeight() - currentHeight
-                    offsetAvailable.fastCoerceAtMost(amountLeft)
-                } else {
-                    val amountLeft = minHeight() - currentHeight
-                    offsetAvailable.fastCoerceAtLeast(amountLeft)
-                }
-            onHeightChanged(currentHeight + amountConsumed)
-            amountConsumed
-        },
-        // Don't consume the velocity on pre/post fling
-        onStop = { 0f },
-        onCancel = { /* do nothing */ },
+        onStart = { LargeTopAppBarScrollController(height, maxHeight, minHeight, onHeightChanged) },
     )
 }
+
+private class LargeTopAppBarScrollController(
+    val height: () -> Float,
+    val maxHeight: () -> Float,
+    val minHeight: () -> Float,
+    val onHeightChanged: (Float) -> Unit,
+) : ScrollController {
+    override fun onScroll(deltaScroll: Float, source: NestedScrollSource): Float {
+        val currentHeight = height()
+        val amountConsumed =
+            if (deltaScroll > 0) {
+                val amountLeft = maxHeight() - currentHeight
+                deltaScroll.fastCoerceAtMost(amountLeft)
+            } else {
+                val amountLeft = minHeight() - currentHeight
+                deltaScroll.fastCoerceAtLeast(amountLeft)
+            }
+        onHeightChanged(currentHeight + amountConsumed)
+        return amountConsumed
+    }
+
+    override suspend fun onStop(initialVelocity: Float): Float {
+        // Don't consume the velocity on pre/post fling
+        return 0f
+    }
+
+    override fun onCancel() {
+        // do nothing
+    }
+
+    override fun canStopOnPreFling() = false
+}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/PriorityNestedScrollConnection.kt b/packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/PriorityNestedScrollConnection.kt
index 57d236b..ca44a5c 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/PriorityNestedScrollConnection.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/PriorityNestedScrollConnection.kt
@@ -16,46 +16,106 @@
 
 package com.android.compose.nestedscroll
 
-import androidx.compose.animation.core.AnimationState
-import androidx.compose.animation.core.DecayAnimationSpec
-import androidx.compose.animation.core.animateDecay
 import androidx.compose.foundation.gestures.Orientation
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
 import androidx.compose.ui.input.nestedscroll.NestedScrollSource
 import androidx.compose.ui.unit.Velocity
 import com.android.compose.ui.util.SpaceVectorConverter
-import kotlin.math.abs
 import kotlin.math.sign
-import kotlinx.coroutines.CompletableDeferred
 import kotlinx.coroutines.Deferred
 import kotlinx.coroutines.async
 import kotlinx.coroutines.coroutineScope
 
 /**
- * A [NestedScrollConnection] that intercepts scroll events in priority mode.
+ * The [ScrollController] provides control over the scroll gesture. It allows you to:
+ * - Scroll the content by a given pixel amount.
+ * - Cancel the current scroll operation.
+ * - Stop the scrolling with a given initial velocity.
  *
- * Priority mode allows this connection to take control over scroll events within a nested scroll
- * hierarchy. When in priority mode, this connection consumes scroll events before its children,
- * enabling custom scrolling behaviors like sticky headers.
+ * **Important Notes:**
+ * - [onCancel] is called only when [PriorityNestedScrollConnection.reset] is invoked or when
+ *   [canCancelScroll] returns `true` after a call to [onScroll]. It is never called after [onStop].
+ * - [onStop] can be interrupted by a new gesture. In such cases, you need to handle a potential
+ *   cancellation within your implementation of [onStop], although [onCancel] will not be called.
+ */
+interface ScrollController {
+    /**
+     * Scrolls the current content by [deltaScroll] pixels.
+     *
+     * @param deltaScroll The amount of pixels to scroll by.
+     * @param source The source of the scroll event.
+     * @return The amount of [deltaScroll] that was consumed.
+     */
+    fun onScroll(deltaScroll: Float, source: NestedScrollSource): Float
+
+    /**
+     * Checks if the current scroll operation can be canceled. This is typically called after
+     * [onScroll] to determine if the [ScrollController] has lost priority and should cancel the
+     * ongoing scroll operation.
+     *
+     * @param available The total amount of scroll available.
+     * @param consumed The amount of scroll consumed by [onScroll].
+     * @return `true` if the scroll can be canceled.
+     */
+    fun canCancelScroll(available: Float, consumed: Float): Boolean {
+        return consumed == 0f
+    }
+
+    /**
+     * Cancels the current scroll operation. This method is called when
+     * [PriorityNestedScrollConnection.reset] is invoked or when [canCancelScroll] returns `true`.
+     */
+    fun onCancel()
+
+    /**
+     * Checks if the scroll can be stopped during the [NestedScrollConnection.onPreFling] phase.
+     *
+     * @return `true` if the scroll can be stopped.
+     */
+    fun canStopOnPreFling(): Boolean
+
+    /**
+     * Stops the controller with the given [initialVelocity]. This typically starts a decay
+     * animation to smoothly bring the scrolling to a stop. This method can be interrupted by a new
+     * gesture, requiring you to handle potential cancellation within your implementation.
+     *
+     * @param initialVelocity The initial velocity of the scroll when stopping.
+     * @return The consumed [initialVelocity] when the animation completes.
+     */
+    suspend fun onStop(initialVelocity: Float): Float
+}
+
+/**
+ * A [NestedScrollConnection] that lets you implement custom scroll behaviors that take priority
+ * over the default nested scrolling logic.
+ *
+ * When started, this connection intercepts scroll events *before* they reach child composables.
+ * This "priority mode" is activated activated when either [canStartPreScroll], [canStartPostScroll]
+ * or [canStartPostFling] returns `true`.
+ *
+ * Once started, the [onStart] lambda provides a [ScrollController] to manage the scrolling. This
+ * controller allows you to directly manipulate the scroll state and define how scroll events are
+ * consumed.
+ *
+ * **Important Considerations:**
+ * - When started, scroll events are typically consumed in `onPreScroll`.
+ * - The provided [ScrollController] should handle potential cancellation of `onStop` due to new
+ *   gestures.
+ * - Use [reset] to release the current [ScrollController] and reset the connection to its initial
+ *   state.
  *
  * @param orientation The orientation of the scroll.
- * @param canStartPreScroll lambda that returns true if the connection can start consuming scroll
- *   events in pre-scroll mode.
- * @param canStartPostScroll lambda that returns true if the connection can start consuming scroll
- *   events in post-scroll mode.
- * @param canStartPostFling lambda that returns true if the connection can start consuming scroll
- *   events in post-fling mode.
- * @param canStopOnScroll lambda that returns true if the connection can stop consuming scroll
- *   events in scroll mode.
- * @param canStopOnPreFling lambda that returns true if the connection can stop consuming scroll
- *   events in pre-fling (i.e. as soon as the user lifts their fingers).
- * @param onStart lambda that is called when the connection starts consuming scroll events.
- * @param onScroll lambda that is called when the connection consumes a scroll event and returns the
- *   consumed amount.
- * @param onStop lambda that is called when the connection stops consuming scroll events and returns
- *   the consumed velocity.
- * @param onCancel lambda that is called when the connection is cancelled.
+ * @param canStartPreScroll A lambda that returns `true` if the connection should enter priority
+ *   mode during the pre-scroll phase. This is called before child connections have a chance to
+ *   consume the scroll.
+ * @param canStartPostScroll A lambda that returns `true` if the connection should enter priority
+ *   mode during the post-scroll phase. This is called after child connections have consumed the
+ *   scroll.
+ * @param canStartPostFling A lambda that returns `true` if the connection should enter priority
+ *   mode during the post-fling phase. This is called after a fling gesture has been initiated.
+ * @param onStart A lambda that is called when the connection enters priority mode. It should return
+ *   a [ScrollController] that will be used to control the scroll.
  * @sample LargeTopAppBarNestedScrollConnection
  * @sample com.android.compose.animation.scene.NestedScrollHandlerImpl.nestedScrollConnection
  */
@@ -66,169 +126,213 @@
     private val canStartPostScroll:
         (offsetAvailable: Float, offsetBeforeStart: Float, source: NestedScrollSource) -> Boolean,
     private val canStartPostFling: (velocityAvailable: Float) -> Boolean,
-    private val canStopOnScroll: (available: Float, consumed: Float) -> Boolean = { _, consumed ->
-        consumed == 0f
-    },
-    private val canStopOnPreFling: () -> Boolean,
-    private val onStart: (offsetAvailable: Float) -> Unit,
-    private val onScroll: (offsetAvailable: Float, source: NestedScrollSource) -> Float,
-    private val onStop: suspend (velocityAvailable: Float) -> Float,
-    private val onCancel: () -> Unit,
+    private val onStart: (firstScroll: Float) -> ScrollController,
 ) : NestedScrollConnection, SpaceVectorConverter by SpaceVectorConverter(orientation) {
 
-    /** In priority mode [onPreScroll] events are first consumed by the parent, via [onScroll]. */
-    private var isPriorityMode = false
+    /** The currently active [ScrollController], or `null` if not in priority mode. */
+    private var currentController: ScrollController? = null
 
+    /**
+     * A [Deferred] representing the ongoing `onStop` animation. Used to interrupt the animation if
+     * a new gesture occurs.
+     */
+    private var stoppingJob: Deferred<Float>? = null
+
+    /**
+     * Indicates whether the connection is currently in the process of stopping the scroll with the
+     * [ScrollController.onStop] animation.
+     */
+    private val isStopping
+        get() = stoppingJob?.isActive ?: false
+
+    /**
+     * Tracks the cumulative scroll offset that has been consumed by other composables before this
+     * connection enters priority mode. This is used to determine when the connection should take
+     * over scrolling based on the [canStartPreScroll] and [canStartPostScroll] conditions.
+     */
     private var offsetScrolledBeforePriorityMode = 0f
 
-    /** This job allows us to interrupt the onStop animation */
-    private var onStopJob: Deferred<Float> = CompletableDeferred(0f)
+    override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
+        // If stopping, interrupt the animation and clear the controller.
+        if (isStopping) {
+            interruptStopping()
+        }
+
+        // If in priority mode, consume the scroll using the current controller.
+        if (currentController != null) {
+            return scroll(available.toFloat(), source)
+        }
+
+        // Check if pre-scroll condition is met, and start priority mode if necessary.
+        val availableFloat = available.toFloat()
+        if (canStartPreScroll(availableFloat, offsetScrolledBeforePriorityMode, source)) {
+            start(availableFloat)
+            return scroll(availableFloat, source)
+        }
+
+        // Track offset consumed before entering priority mode.
+        offsetScrolledBeforePriorityMode += availableFloat
+        return Offset.Zero
+    }
 
     override fun onPostScroll(
         consumed: Offset,
         available: Offset,
         source: NestedScrollSource,
     ): Offset {
+        // If in priority mode, scroll events are consumed only in pre-scroll phase.
+        if (currentController != null) return Offset.Zero
+
+        // Check if post-scroll condition is met, and start priority mode if necessary.
         val availableFloat = available.toFloat()
-        // The offset before the start takes into account the up and down movements, starting from
-        // the beginning or from the last fling gesture.
         val offsetBeforeStart = offsetScrolledBeforePriorityMode - availableFloat
-
-        if (isPriorityMode || !canStartPostScroll(availableFloat, offsetBeforeStart, source)) {
-            // The priority mode cannot start so we won't consume the available offset.
-            return Offset.Zero
+        if (canStartPostScroll(availableFloat, offsetBeforeStart, source)) {
+            start(availableFloat)
+            return scroll(availableFloat, source)
         }
 
-        return start(availableFloat, source).toOffset()
-    }
-
-    override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
-        if (!isPriorityMode) {
-            val availableFloat = available.toFloat()
-            if (canStartPreScroll(availableFloat, offsetScrolledBeforePriorityMode, source)) {
-                return start(availableFloat, source).toOffset()
-            }
-            // We want to track the amount of offset consumed before entering priority mode
-            offsetScrolledBeforePriorityMode += availableFloat
-            return Offset.Zero
-        }
-
-        return scroll(available.toFloat(), source).toOffset()
+        // Do not consume the offset if priority mode is not activated.
+        return Offset.Zero
     }
 
     override suspend fun onPreFling(available: Velocity): Velocity {
-        if (!isPriorityMode) {
-            resetOffsetTracker()
-            return Velocity.Zero
+        val controller = currentController ?: return Velocity.Zero
+
+        // If in priority mode and can stop on pre-fling phase, stop the scroll.
+        if (controller.canStopOnPreFling()) {
+            return stop(velocity = available.toFloat())
         }
 
-        if (canStopOnPreFling()) {
-            // Step 3b: The finger is lifted, we can stop intercepting scroll events and use the
-            // velocity of the fling gesture.
-            return stop(velocityAvailable = available.toFloat()).toVelocity()
-        }
-
-        // We don't want to consume the velocity, we prefer to continue receiving scroll events.
+        // Do not consume the velocity if not stopping on pre-fling phase.
         return Velocity.Zero
     }
 
     override suspend fun onPostFling(consumed: Velocity, available: Velocity): Velocity {
         val availableFloat = available.toFloat()
-        if (isPriorityMode) {
-            return stop(velocityAvailable = availableFloat).toVelocity()
+        val controller = currentController
+
+        // If in priority mode, stop the scroll.
+        if (controller != null) {
+            return stop(velocity = availableFloat)
         }
 
-        if (!canStartPostFling(availableFloat)) {
-            return Velocity.Zero
-        }
-
-        // The offset passed to onPriorityStart() must be != 0f, so we create a small offset of 1px
-        // given the available velocity.
+        // Check if post-fling condition is met, and start priority mode if necessary.
         // TODO(b/291053278): Remove canStartPostFling() and instead make it possible to define the
         // overscroll behavior on the Scene level.
-        val smallOffset = availableFloat.sign
-        start(
-            availableOffset = smallOffset,
-            source = NestedScrollSource.SideEffect,
-            skipScroll = true,
-        )
+        if (canStartPostFling(availableFloat)) {
+            // The offset passed to onPriorityStart() must be != 0f, so we create a small offset of
+            // 1px given the available velocity.
+            val smallOffset = availableFloat.sign
+            start(availableOffset = smallOffset)
+            return stop(availableFloat)
+        }
 
-        // This is the last event of a scroll gesture.
-        return stop(availableFloat).toVelocity()
+        // Reset offset tracking after the fling gesture is finished.
+        resetOffsetTracker()
+        return Velocity.Zero
     }
 
     /**
-     * Method to call before destroying the object or to reset the initial state.
-     *
-     * TODO(b/303224944) This method should be removed.
+     * Resets the connection to its initial state. This cancels any ongoing scroll operation and
+     * clears the current [ScrollController].
      */
     fun reset() {
-        if (isPriorityMode) {
-            // Step 3c: To ensure that an onStop (or onCancel) is always called for every onStart.
+        if (currentController != null && !isStopping) {
             cancel()
         } else {
             resetOffsetTracker()
         }
     }
 
-    private fun start(
-        availableOffset: Float,
-        source: NestedScrollSource,
-        skipScroll: Boolean = false,
-    ): Float {
-        check(!isPriorityMode) {
-            "This should never happen, start() was called when isPriorityMode"
-        }
+    /**
+     * Starts priority mode by creating a new [ScrollController] using the [onStart] lambda.
+     *
+     * @param availableOffset The initial scroll offset available.
+     */
+    private fun start(availableOffset: Float) {
+        check(currentController == null) { "Another controller is active: $currentController" }
 
-        // Step 1: It's our turn! We start capturing scroll events when one of our children has an
-        // available offset following a scroll event.
-        isPriorityMode = true
+        resetOffsetTracker()
 
-        onStopJob.cancel()
-
-        // Note: onStop will be called if we cannot continue to scroll (step 3a), or the finger is
-        // lifted (step 3b), or this object has been destroyed (step 3c).
-        onStart(availableOffset)
-
-        return if (skipScroll) 0f else scroll(availableOffset, source)
+        currentController = onStart(availableOffset)
     }
 
-    private fun scroll(offsetAvailable: Float, source: NestedScrollSource): Float {
-        // Step 2: We have the priority and can consume the scroll events.
-        val consumedByScroll = onScroll(offsetAvailable, source)
+    /**
+     * Retrieves the current [ScrollController], ensuring that it is not null and that the
+     * [isStopping] state matches the expected value.
+     */
+    private fun requireController(isStopping: Boolean): ScrollController {
+        check(this.isStopping == isStopping) {
+            "isStopping is ${this.isStopping}, instead of $isStopping"
+        }
+        check(offsetScrolledBeforePriorityMode == 0f) {
+            "offset scrolled should be zero, but it was $offsetScrolledBeforePriorityMode"
+        }
+        return checkNotNull(currentController) { "The controller is $currentController" }
+    }
 
-        if (canStopOnScroll(offsetAvailable, consumedByScroll)) {
-            // Step 3a: We have lost priority and we no longer need to intercept scroll events.
+    /**
+     * Scrolls the content using the current [ScrollController].
+     *
+     * @param delta The amount of scroll to apply.
+     * @param source The source of the scroll event.
+     * @return The amount of scroll consumed.
+     */
+    private fun scroll(delta: Float, source: NestedScrollSource): Offset {
+        val controller = requireController(isStopping = false)
+        val consumedByScroll = controller.onScroll(delta, source)
+
+        if (controller.canCancelScroll(delta, consumedByScroll)) {
+            // We have lost priority and we no longer need to intercept scroll events.
             cancel()
-
-            // We've just reset offsetScrolledBeforePriorityMode to 0f
-            // We want to track the amount of offset consumed before entering priority mode
-            offsetScrolledBeforePriorityMode += offsetAvailable - consumedByScroll
+            offsetScrolledBeforePriorityMode = delta - consumedByScroll
         }
 
-        return consumedByScroll
+        return consumedByScroll.toOffset()
     }
 
-    /** Reset the tracking of consumed offsets before entering in priority mode. */
+    /** Cancels the current scroll operation and clears the current [ScrollController]. */
+    private fun cancel() {
+        requireController(isStopping = false).onCancel()
+        currentController = null
+    }
+
+    /**
+     * Stops the scroll with the given velocity using the current [ScrollController].
+     *
+     * @param velocity The velocity to stop with.
+     * @return The consumed velocity.
+     */
+    suspend fun stop(velocity: Float): Velocity {
+        val controller = requireController(isStopping = false)
+        return coroutineScope {
+            try {
+                async { controller.onStop(velocity) }
+                    // Allows others to interrupt the job.
+                    .also { stoppingJob = it }
+                    // Note: this can be cancelled by [interruptStopping]
+                    .await()
+                    .toVelocity()
+            } finally {
+                // If the job is interrupted, it might take a while to cancel. We need to make sure
+                // the current controller is still the initial one.
+                if (currentController == controller) {
+                    currentController = null
+                }
+            }
+        }
+    }
+
+    /** Interrupts the ongoing stop animation and clears the current [ScrollController]. */
+    private fun interruptStopping() {
+        requireController(isStopping = true)
+        // We are throwing a CancellationException in the [ScrollController.onStop] method.
+        stoppingJob?.cancel()
+        currentController = null
+    }
+
+    /** Resets the tracking of consumed offsets before entering priority mode. */
     private fun resetOffsetTracker() {
         offsetScrolledBeforePriorityMode = 0f
     }
-
-    private suspend fun stop(velocityAvailable: Float): Float {
-        check(isPriorityMode) { "This should never happen, stop() was called before start()" }
-        isPriorityMode = false
-        resetOffsetTracker()
-
-        return coroutineScope {
-            onStopJob = async { onStop(velocityAvailable) }
-            onStopJob.await()
-        }
-    }
-
-    private fun cancel() {
-        check(isPriorityMode) { "This should never happen, cancel() was called before start()" }
-        isPriorityMode = false
-        resetOffsetTracker()
-        onCancel()
-    }
 }
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/NestedScrollToSceneTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/NestedScrollToSceneTest.kt
index 5edb99e..37dae39 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/NestedScrollToSceneTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/NestedScrollToSceneTest.kt
@@ -225,6 +225,40 @@
     }
 
     @Test
+    fun stlNotConsumeUnobservedGesture() {
+        val state =
+            rule.runOnUiThread {
+                MutableSceneTransitionLayoutState(SceneA, transitions = EmptyTestTransitions)
+            }
+
+        rule.setContent {
+            touchSlop = LocalViewConfiguration.current.touchSlop
+            SceneTransitionLayout(
+                state = state,
+                modifier = Modifier.size(layoutWidth, layoutHeight),
+            ) {
+                scene(SceneA) {
+                    Spacer(
+                        Modifier.verticalNestedScrollToScene()
+                            // This scrollable will not consume the gesture.
+                            .scrollable(rememberScrollableState { 0f }, Vertical)
+                            .fillMaxSize()
+                    )
+                }
+            }
+        }
+
+        rule.onRoot().performTouchInput {
+            down(Offset.Zero)
+            // There is no vertical scene.
+            moveBy(Offset(0f, layoutWidth.toPx()), delayMillis = 1_000)
+        }
+
+        rule.waitForIdle()
+        assertThat(state.transitionState).isIdle()
+    }
+
+    @Test
     fun customizeStlNestedScrollBehavior_multipleRequests() {
         var canScroll = true
         val state = setup2ScenesAndScrollTouchSlop {
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/nestedscroll/PriorityNestedScrollConnectionTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/nestedscroll/PriorityNestedScrollConnectionTest.kt
index 1a3b86b..0364cdc 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/nestedscroll/PriorityNestedScrollConnectionTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/nestedscroll/PriorityNestedScrollConnectionTest.kt
@@ -20,6 +20,7 @@
 
 import androidx.compose.foundation.gestures.Orientation
 import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.input.nestedscroll.NestedScrollSource
 import androidx.compose.ui.input.nestedscroll.NestedScrollSource.Companion.UserInput
 import androidx.compose.ui.unit.Velocity
 import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -48,17 +49,26 @@
             canStartPreScroll = { _, _, _ -> canStartPreScroll },
             canStartPostScroll = { _, _, _ -> canStartPostScroll },
             canStartPostFling = { canStartPostFling },
-            canStopOnPreFling = { canStopOnPreFling },
-            onStart = { isStarted = true },
-            onScroll = { offsetAvailable, _ ->
-                lastScroll = offsetAvailable
-                if (consumeScroll) offsetAvailable else 0f
+            onStart = { _ ->
+                isStarted = true
+                object : ScrollController {
+                    override fun onScroll(deltaScroll: Float, source: NestedScrollSource): Float {
+                        lastScroll = deltaScroll
+                        return if (consumeScroll) deltaScroll else 0f
+                    }
+
+                    override suspend fun onStop(initialVelocity: Float): Float {
+                        lastStop = initialVelocity
+                        return if (consumeStop) initialVelocity else 0f
+                    }
+
+                    override fun onCancel() {
+                        isCancelled = true
+                    }
+
+                    override fun canStopOnPreFling() = canStopOnPreFling
+                }
             },
-            onStop = {
-                lastStop = it
-                if (consumeStop) it else 0f
-            },
-            onCancel = { isCancelled = true },
         )
 
     @Test
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt
index 8e838f3..a89e6fb 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt
@@ -19,12 +19,14 @@
 import com.android.systemui.customization.R
 import com.android.systemui.log.core.LogLevel
 import com.android.systemui.log.core.LogcatOnlyMessageBuffer
+import com.android.systemui.plugins.clocks.AxisType
 import com.android.systemui.plugins.clocks.ClockController
 import com.android.systemui.plugins.clocks.ClockId
 import com.android.systemui.plugins.clocks.ClockMessageBuffers
 import com.android.systemui.plugins.clocks.ClockMetadata
 import com.android.systemui.plugins.clocks.ClockPickerConfig
 import com.android.systemui.plugins.clocks.ClockProvider
+import com.android.systemui.plugins.clocks.ClockReactiveAxis
 import com.android.systemui.plugins.clocks.ClockSettings
 import com.android.systemui.shared.clocks.view.HorizontalAlignment
 import com.android.systemui.shared.clocks.view.VerticalAlignment
@@ -85,8 +87,21 @@
             // TODO(b/352049256): Update placeholder to actual resource
             resources.getDrawable(R.drawable.clock_default_thumbnail, null),
             isReactiveToTone = true,
-            isReactiveToTouch = isClockReactiveVariantsEnabled,
-            axes = listOf(), // TODO: Ater some picker definition
+            // TODO(b/364673969): Populate the rest of this
+            axes =
+                if (isClockReactiveVariantsEnabled)
+                    listOf(
+                        ClockReactiveAxis(
+                            key = "wdth",
+                            type = AxisType.Slider,
+                            maxValue = 1000f,
+                            minValue = 100f,
+                            currentValue = 400f,
+                            name = "Width",
+                            description = "Glyph Width",
+                        )
+                    )
+                else listOf(),
         )
     }
 
diff --git a/packages/SystemUI/log/Android.bp b/packages/SystemUI/log/Android.bp
index 2f1d354..afdcae48 100644
--- a/packages/SystemUI/log/Android.bp
+++ b/packages/SystemUI/log/Android.bp
@@ -22,19 +22,15 @@
     default_applicable_licenses: ["frameworks_base_packages_SystemUI_license"],
 }
 
-android_library {
+java_library {
     name: "SystemUILogLib",
-    use_resource_processor: true,
     srcs: [
         "src/**/*.java",
         "src/**/*.kt",
     ],
     static_libs: [
-        "androidx.core_core-ktx",
-        "androidx.annotation_annotation",
         "error_prone_annotations",
         "SystemUICommon",
     ],
-    manifest: "AndroidManifest.xml",
     kotlincflags: ["-Xjvm-default=all"],
 }
diff --git a/packages/SystemUI/log/AndroidManifest.xml b/packages/SystemUI/log/AndroidManifest.xml
deleted file mode 100644
index 4021e1a..0000000
--- a/packages/SystemUI/log/AndroidManifest.xml
+++ /dev/null
@@ -1,22 +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.
--->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.android.systemui.log">
-
-
-</manifest>
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/ActiveUnlockConfigTest.kt b/packages/SystemUI/multivalentTests/src/com/android/keyguard/ActiveUnlockConfigTest.kt
similarity index 99%
rename from packages/SystemUI/tests/src/com/android/keyguard/ActiveUnlockConfigTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/keyguard/ActiveUnlockConfigTest.kt
index 48f6cc4..14d34d7 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/ActiveUnlockConfigTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/keyguard/ActiveUnlockConfigTest.kt
@@ -33,6 +33,7 @@
 import android.provider.Settings.Secure.ACTIVE_UNLOCK_ON_WAKE
 import android.provider.Settings.Secure.ACTIVE_UNLOCK_WAKEUPS_CONSIDERED_UNLOCK_INTENTS
 import android.provider.Settings.Secure.ACTIVE_UNLOCK_WAKEUPS_TO_FORCE_DISMISS_KEYGUARD
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.dump.DumpManager
@@ -47,6 +48,7 @@
 import org.junit.Assert.assertTrue
 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
@@ -55,6 +57,7 @@
 import org.mockito.MockitoAnnotations
 
 @SmallTest
+@RunWith(AndroidJUnit4::class)
 class ActiveUnlockConfigTest : SysuiTestCase() {
     private lateinit var secureSettings: FakeSettings
     @Mock private lateinit var contentResolver: ContentResolver
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockAccessibilityDelegateTest.java b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardClockAccessibilityDelegateTest.java
similarity index 96%
rename from packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockAccessibilityDelegateTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardClockAccessibilityDelegateTest.java
index edf29c5..b937db6 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockAccessibilityDelegateTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardClockAccessibilityDelegateTest.java
@@ -25,6 +25,7 @@
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.widget.TextView;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
@@ -32,10 +33,12 @@
 
 import org.junit.Before;
 import org.junit.Test;
+import org.junit.runner.RunWith;
 
 import java.util.List;
 
 @SmallTest
+@RunWith(AndroidJUnit4.class)
 public class KeyguardClockAccessibilityDelegateTest extends SysuiTestCase {
 
     private TextView mView;
@@ -111,4 +114,4 @@
     private boolean isEmpty(List<CharSequence> texts) {
         return texts.stream().allMatch(TextUtils::isEmpty);
     }
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt
index 5fe5cb3..1841d2e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt
@@ -623,6 +623,22 @@
     }
 
     @Test
+    fun showNextSecurityScreenOrFinish_calledWithNoAuthentication_butRequiresSimPin() {
+        // GIVEN trust is true (extended unlock)
+        whenever(keyguardUpdateMonitor.getUserHasTrust(anyInt())).thenReturn(true)
+
+        // WHEN SIMPIN is the next required screen
+        whenever(keyguardSecurityModel.getSecurityMode(TARGET_USER_ID))
+            .thenReturn(SecurityMode.SimPin)
+
+        // WHEN a request is made to dismiss
+        underTest.dismiss(TARGET_USER_ID)
+
+        // THEN we will show the SIM PIN screen
+        verify(viewFlipperController).getSecurityView(eq(SecurityMode.SimPin), any(), any())
+    }
+
+    @Test
     fun onSwipeUp_forwardsItToFaceAuthInteractor() {
         val registeredSwipeListener = registeredSwipeListener
         setupGetSecurityView(SecurityMode.Password)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/DependencyTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/DependencyTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/DependencyTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/DependencyTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ExpandHelperTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/ExpandHelperTest.java
similarity index 97%
rename from packages/SystemUI/tests/src/com/android/systemui/ExpandHelperTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/ExpandHelperTest.java
index ccdcee5..1b07241 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ExpandHelperTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/ExpandHelperTest.java
@@ -21,12 +21,12 @@
 import static org.mockito.Mockito.when;
 
 import android.content.Context;
-import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.testing.TestableLooper.RunWithLooper;
 
 import androidx.core.animation.ObjectAnimator;
 import androidx.test.annotation.UiThreadTest;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.keyguard.KeyguardUpdateMonitor;
@@ -42,7 +42,7 @@
 import org.junit.runner.RunWith;
 
 @SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 @RunWithLooper
 public class ExpandHelperTest extends SysuiTestCase {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/InitControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/InitControllerTest.java
similarity index 95%
rename from packages/SystemUI/tests/src/com/android/systemui/InitControllerTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/InitControllerTest.java
index e0ca87c..e0c7c7a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/InitControllerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/InitControllerTest.java
@@ -19,16 +19,16 @@
 import static junit.framework.TestCase.assertFalse;
 import static junit.framework.TestCase.assertTrue;
 
-import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
 @SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 @TestableLooper.RunWithLooper
 public class InitControllerTest extends SysuiTestCase {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityTargetAdapterTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityTargetAdapterTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityTargetAdapterTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityTargetAdapterTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuViewTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuViewTest.java
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/ShadeTouchHandlerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/ShadeTouchHandlerTest.kt
index ad636cf..fa5af51 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/ShadeTouchHandlerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/ShadeTouchHandlerTest.kt
@@ -151,8 +151,14 @@
 
     // Verifies that a swipe down forwards captured touches to the window root view for handling.
     @Test
-    @EnableFlags(Flags.FLAG_COMMUNAL_HUB, Flags.FLAG_SCENE_CONTAINER)
+    @EnableFlags(
+        Flags.FLAG_COMMUNAL_HUB,
+        Flags.FLAG_SCENE_CONTAINER,
+        Flags.FLAG_HUBMODE_FULLSCREEN_VERTICAL_SWIPE_FIX,
+    )
     fun testSwipeDown_sceneContainerEnabled_sentToWindowRootView() {
+        mTouchHandler.onGlanceableTouchAvailable(true)
+
         swipe(Direction.DOWN)
 
         // Both motion events are sent for central surfaces to process.
@@ -165,8 +171,7 @@
     @Test
     @EnableFlags(Flags.FLAG_SCENE_CONTAINER)
     @DisableFlags(Flags.FLAG_HUBMODE_FULLSCREEN_VERTICAL_SWIPE_FIX)
-    fun testSwipeDown_dreaming_sentToWindowRootView() {
-        whenever(mDreamManager.isDreaming).thenReturn(true)
+    fun testSwipeDown_sceneContainerEnabledFullscreenSwipeDisabled_sentToWindowRootView() {
         swipe(Direction.DOWN)
 
         // Both motion events are sent for the shade view to process.
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/AnimatorTestRuleOrderTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/animation/AnimatorTestRuleOrderTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/animation/AnimatorTestRuleOrderTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/animation/AnimatorTestRuleOrderTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/AudioSharingDialogDelegateTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bluetooth/qsdialog/AudioSharingDialogDelegateTest.kt
similarity index 97%
rename from packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/AudioSharingDialogDelegateTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/bluetooth/qsdialog/AudioSharingDialogDelegateTest.kt
index 25b85b5..9f0c7e1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/AudioSharingDialogDelegateTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bluetooth/qsdialog/AudioSharingDialogDelegateTest.kt
@@ -16,7 +16,7 @@
 
 package com.android.systemui.bluetooth.qsdialog
 
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import android.testing.TestableLooper
 import android.widget.Button
 import android.widget.TextView
@@ -43,7 +43,7 @@
 import org.mockito.kotlin.whenever
 
 @SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
 @OptIn(ExperimentalCoroutinesApi::class)
 class AudioSharingDialogDelegateTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemActionInteractorTest.kt
similarity index 98%
rename from packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemActionInteractorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemActionInteractorTest.kt
index 9c427c6..44f9720 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemActionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemActionInteractorTest.kt
@@ -15,7 +15,7 @@
  */
 package com.android.systemui.bluetooth.qsdialog
 
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import android.testing.TestableLooper
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
@@ -37,7 +37,7 @@
 import org.mockito.kotlin.whenever
 
 @SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
 @OptIn(ExperimentalCoroutinesApi::class)
 class DeviceItemActionInteractorTest : SysuiTestCase() {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/BouncerActionButtonInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/BouncerActionButtonInteractorTest.kt
index e3b5f34..566cd70 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/BouncerActionButtonInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/BouncerActionButtonInteractorTest.kt
@@ -27,6 +27,7 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.authentication.data.repository.fakeAuthenticationRepository
 import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
+import com.android.systemui.bouncer.shared.model.BouncerActionButtonModel
 import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.flags.EnableSceneContainer
@@ -83,7 +84,7 @@
         overrideResource(R.string.lockscreen_return_to_call, MESSAGE_RETURN_TO_CALL)
         overrideResource(
             R.bool.config_enable_emergency_call_while_sim_locked,
-            ENABLE_EMERGENCY_CALL_WHILE_SIM_LOCKED
+            ENABLE_EMERGENCY_CALL_WHILE_SIM_LOCKED,
         )
         whenever(selectedUserInteractor.getSelectedUserId()).thenReturn(currentUserId)
         whenever(emergencyAffordanceManager.needsEmergencyAffordance())
@@ -123,11 +124,11 @@
             kosmos.fakeTelephonyRepository.setIsInCall(true)
 
             assertThat(actionButton).isNotNull()
-            assertThat(actionButton?.label).isEqualTo(MESSAGE_RETURN_TO_CALL)
-            assertThat(actionButton?.onClick).isNotNull()
-            assertThat(actionButton?.onLongClick).isNull()
+            assertThat(actionButton?.labelResId).isEqualTo(R.string.lockscreen_return_to_call)
+            assertThat(actionButton)
+                .isInstanceOf(BouncerActionButtonModel.ReturnToCallButtonModel::class.java)
 
-            actionButton?.onClick?.invoke()
+            underTest.onReturnToCallButtonClicked()
             runCurrent()
 
             assertThat(metricsLogger.logs.size).isEqualTo(1)
@@ -150,11 +151,11 @@
             kosmos.fakeTelephonyRepository.setIsInCall(false)
 
             assertThat(actionButton).isNotNull()
-            assertThat(actionButton?.label).isEqualTo(MESSAGE_EMERGENCY_CALL)
-            assertThat(actionButton?.onClick).isNotNull()
-            assertThat(actionButton?.onLongClick).isNotNull()
+            assertThat(actionButton?.labelResId).isEqualTo(R.string.lockscreen_emergency_call)
+            assertThat(actionButton)
+                .isInstanceOf(BouncerActionButtonModel.EmergencyButtonModel::class.java)
 
-            actionButton?.onClick?.invoke()
+            underTest.onEmergencyButtonClicked()
             runCurrent()
 
             assertThat(metricsLogger.logs.size).isEqualTo(1)
@@ -167,12 +168,12 @@
             //  ActivityStarter interface here.
             verify(emergencyAffordanceManager, never()).performEmergencyCall()
 
-            actionButton?.onLongClick?.invoke()
+            underTest.onEmergencyButtonLongClicked()
             verify(emergencyAffordanceManager).performEmergencyCall()
         }
 
     @Test
-    fun noCall_insecureAuthMethodButSecureSim_emergencyCallButton() =
+    fun noCall_insecureAuthMethodButSecureSim_emergencyCallButtonIsActionButton() =
         testScope.runTest {
             val underTest = kosmos.bouncerActionButtonInteractor
             val actionButton by collectLastValue(underTest.actionButton)
@@ -184,9 +185,9 @@
             runCurrent()
 
             assertThat(actionButton).isNotNull()
-            assertThat(actionButton?.label).isEqualTo(MESSAGE_EMERGENCY_CALL)
-            assertThat(actionButton?.onClick).isNotNull()
-            assertThat(actionButton?.onLongClick).isNotNull()
+            assertThat(actionButton?.labelResId).isEqualTo(R.string.lockscreen_emergency_call)
+            assertThat(actionButton)
+                .isInstanceOf(BouncerActionButtonModel.EmergencyButtonModel::class.java)
         }
 
     @Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt
index 361b078..521b346 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt
@@ -16,6 +16,8 @@
 
 package com.android.systemui.bouncer.domain.interactor
 
+import android.content.testableContext
+import android.provider.Settings.Global.ONE_HANDED_KEYGUARD_SIDE
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.internal.logging.uiEventLoggerFake
@@ -26,16 +28,22 @@
 import com.android.systemui.authentication.domain.interactor.authenticationInteractor
 import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
 import com.android.systemui.authentication.shared.model.AuthenticationPatternCoordinate
+import com.android.systemui.authentication.shared.model.BouncerInputSide
+import com.android.systemui.bouncer.data.repository.bouncerRepository
 import com.android.systemui.bouncer.shared.logging.BouncerUiEvent
+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.deviceentry.domain.interactor.deviceEntryFaceAuthInteractor
 import com.android.systemui.flags.EnableSceneContainer
+import com.android.systemui.flags.Flags.FULL_SCREEN_USER_SWITCHER
+import com.android.systemui.flags.fakeFeatureFlagsClassic
 import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFaceAuthRepository
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.power.data.repository.fakePowerRepository
 import com.android.systemui.res.R
 import com.android.systemui.testKosmos
+import com.android.systemui.util.settings.fakeGlobalSettings
 import com.google.common.truth.Truth.assertThat
 import kotlin.time.Duration.Companion.seconds
 import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -58,7 +66,8 @@
     private val authenticationInteractor = kosmos.authenticationInteractor
     private val uiEventLoggerFake = kosmos.uiEventLoggerFake
 
-    private lateinit var underTest: BouncerInteractor
+    private val underTest: BouncerInteractor by lazy { kosmos.bouncerInteractor }
+    private val testableResources by lazy { kosmos.testableContext.orCreateTestableResources }
 
     @Before
     fun setUp() {
@@ -70,8 +79,6 @@
         overrideResource(R.string.kg_wrong_pin, MESSAGE_WRONG_PIN)
         overrideResource(R.string.kg_wrong_password, MESSAGE_WRONG_PASSWORD)
         overrideResource(R.string.kg_wrong_pattern, MESSAGE_WRONG_PATTERN)
-
-        underTest = kosmos.bouncerInteractor
     }
 
     @Test
@@ -116,7 +123,7 @@
             assertThat(
                     underTest.authenticate(
                         FakeAuthenticationRepository.DEFAULT_PIN,
-                        tryAutoConfirm = true
+                        tryAutoConfirm = true,
                     )
                 )
                 .isEqualTo(AuthenticationResult.SUCCEEDED)
@@ -141,7 +148,7 @@
             assertThat(
                     underTest.authenticate(
                         FakeAuthenticationRepository.DEFAULT_PIN,
-                        tryAutoConfirm = true
+                        tryAutoConfirm = true,
                     )
                 )
                 .isEqualTo(AuthenticationResult.SKIPPED)
@@ -209,7 +216,7 @@
             val tooShortPattern =
                 FakeAuthenticationRepository.PATTERN.subList(
                     0,
-                    kosmos.fakeAuthenticationRepository.minPatternLength - 1
+                    kosmos.fakeAuthenticationRepository.minPatternLength - 1,
                 )
             assertThat(underTest.authenticate(tooShortPattern))
                 .isEqualTo(AuthenticationResult.SKIPPED)
@@ -292,6 +299,77 @@
             assertThat(isFaceAuthRunning).isFalse()
         }
 
+    @Test
+    fun verifyOneHandedModeUsesTheConfigValue() =
+        testScope.runTest {
+            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
+                AuthenticationMethodModel.Pin
+            )
+            testableResources.addOverride(R.bool.can_use_one_handed_bouncer, false)
+            val oneHandedModelSupported by collectLastValue(underTest.isOneHandedModeSupported)
+
+            assertThat(oneHandedModelSupported).isFalse()
+
+            testableResources.addOverride(R.bool.can_use_one_handed_bouncer, true)
+            kosmos.fakeConfigurationRepository.onAnyConfigurationChange()
+            runCurrent()
+
+            assertThat(oneHandedModelSupported).isTrue()
+
+            testableResources.removeOverride(R.bool.can_use_one_handed_bouncer)
+        }
+
+    @Test
+    fun verifyPreferredInputSideUsesTheSettingValue_Left() =
+        testScope.runTest {
+            val preferredInputSide by collectLastValue(underTest.preferredBouncerInputSide)
+            kosmos.bouncerRepository.setPreferredBouncerInputSide(BouncerInputSide.LEFT)
+            runCurrent()
+
+            assertThat(preferredInputSide).isEqualTo(BouncerInputSide.LEFT)
+        }
+
+    @Test
+    fun verifyPreferredInputSideUsesTheSettingValue_Right() =
+        testScope.runTest {
+            val preferredInputSide by collectLastValue(underTest.preferredBouncerInputSide)
+            underTest.setPreferredBouncerInputSide(BouncerInputSide.RIGHT)
+            runCurrent()
+
+            assertThat(preferredInputSide).isEqualTo(BouncerInputSide.RIGHT)
+
+            underTest.setPreferredBouncerInputSide(BouncerInputSide.LEFT)
+            runCurrent()
+
+            assertThat(preferredInputSide).isEqualTo(BouncerInputSide.LEFT)
+        }
+
+    @Test
+    fun preferredInputSide_defaultsToRight_whenUserSwitcherIsEnabled() =
+        testScope.runTest {
+            testableResources.addOverride(R.bool.config_enableBouncerUserSwitcher, true)
+            kosmos.fakeFeatureFlagsClassic.set(FULL_SCREEN_USER_SWITCHER, true)
+            kosmos.bouncerRepository.preferredBouncerInputSide.value = null
+            val preferredInputSide by collectLastValue(underTest.preferredBouncerInputSide)
+
+            assertThat(preferredInputSide).isEqualTo(BouncerInputSide.RIGHT)
+            testableResources.removeOverride(R.bool.config_enableBouncerUserSwitcher)
+        }
+
+    @Test
+    fun preferredInputSide_defaultsToLeft_whenUserSwitcherIsNotEnabledAndOneHandedModeIsEnabled() =
+        testScope.runTest {
+            testableResources.addOverride(R.bool.config_enableBouncerUserSwitcher, false)
+            kosmos.fakeFeatureFlagsClassic.set(FULL_SCREEN_USER_SWITCHER, true)
+            testableResources.addOverride(R.bool.can_use_one_handed_bouncer, true)
+            kosmos.fakeGlobalSettings.putInt(ONE_HANDED_KEYGUARD_SIDE, -1)
+            val preferredInputSide by collectLastValue(underTest.preferredBouncerInputSide)
+
+            assertThat(preferredInputSide).isEqualTo(BouncerInputSide.LEFT)
+            testableResources.removeOverride(R.bool.config_enableBouncerUserSwitcher)
+            testableResources.removeOverride(R.bool.can_use_one_handed_bouncer)
+        }
+
     companion object {
         private const val MESSAGE_ENTER_YOUR_PIN = "Enter your PIN"
         private const val MESSAGE_ENTER_YOUR_PASSWORD = "Enter your password"
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/helper/BouncerSceneLayoutTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/helper/BouncerSceneLayoutTest.kt
index 923687b..3ede841 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/helper/BouncerSceneLayoutTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/helper/BouncerSceneLayoutTest.kt
@@ -25,10 +25,10 @@
 import com.google.common.truth.Truth.assertThat
 import java.util.Locale
 import org.junit.Test
-import platform.test.runner.parameterized.ParameterizedAndroidJunit4
-import platform.test.runner.parameterized.Parameter
-import platform.test.runner.parameterized.Parameters
 import org.junit.runner.RunWith
+import platform.test.runner.parameterized.Parameter
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
 
 @SmallTest
 @RunWith(ParameterizedAndroidJunit4::class)
@@ -41,6 +41,7 @@
             height = SizeClass.EXPANDED,
             naturallyHeld = Vertically,
         )
+
     data object Tablet :
         Device(
             name = "tablet",
@@ -48,6 +49,7 @@
             height = SizeClass.MEDIUM,
             naturallyHeld = Horizontally,
         )
+
     data object Folded :
         Device(
             name = "folded",
@@ -55,6 +57,7 @@
             height = SizeClass.MEDIUM,
             naturallyHeld = Vertically,
         )
+
     data object Unfolded :
         Device(
             name = "unfolded",
@@ -64,6 +67,7 @@
             widthWhenUnnaturallyHeld = SizeClass.MEDIUM,
             heightWhenUnnaturallyHeld = SizeClass.MEDIUM,
         )
+
     data object TallerFolded :
         Device(
             name = "taller folded",
@@ -71,6 +75,7 @@
             height = SizeClass.EXPANDED,
             naturallyHeld = Vertically,
         )
+
     data object TallerUnfolded :
         Device(
             name = "taller unfolded",
@@ -131,7 +136,7 @@
                                 TestCase(
                                     device = device,
                                     held = device.naturallyHeld,
-                                    isSideBySideSupported = false,
+                                    isOneHandedModeSupported = false,
                                     expected = STANDARD_BOUNCER,
                                 )
                             )
@@ -151,7 +156,7 @@
                                 TestCase(
                                     device = device,
                                     held = device.naturallyHeld.flip(),
-                                    isSideBySideSupported = false,
+                                    isOneHandedModeSupported = false,
                                     expected = STANDARD_BOUNCER,
                                 )
                             )
@@ -170,7 +175,7 @@
                         calculateLayoutInternal(
                             width = device.width(whenHeld = held),
                             height = device.height(whenHeld = held),
-                            isSideBySideSupported = isSideBySideSupported,
+                            isOneHandedModeSupported = isOneHandedModeSupported,
                         )
                     )
                     .isEqualTo(expected)
@@ -182,7 +187,7 @@
         val device: Device,
         val held: Held,
         val expected: BouncerSceneLayout,
-        val isSideBySideSupported: Boolean = true,
+        val isOneHandedModeSupported: Boolean = true,
     ) {
         override fun toString(): String {
             return buildString {
@@ -190,8 +195,8 @@
                 append(" width: ${device.width(held).name.lowercase(Locale.US)}")
                 append(" height: ${device.height(held).name.lowercase(Locale.US)}")
                 append(" when held $held")
-                if (!isSideBySideSupported) {
-                    append(" (side-by-side not supported)")
+                if (!isOneHandedModeSupported) {
+                    append(" (one-handed-mode not supported)")
                 }
             }
         }
@@ -242,11 +247,13 @@
     sealed class Held {
         abstract fun flip(): Held
     }
+
     data object Vertically : Held() {
         override fun flip(): Held {
             return Horizontally
         }
     }
+
     data object Horizontally : Held() {
         override fun flip(): Held {
             return Vertically
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerSceneContentViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerSceneContentViewModelTest.kt
index 9bddcd2..3bf4460 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerSceneContentViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerSceneContentViewModelTest.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.bouncer.ui.viewmodel
 
+import android.content.testableContext
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
@@ -35,6 +36,7 @@
 import com.android.systemui.flags.fakeFeatureFlagsClassic
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.lifecycle.activateIn
+import com.android.systemui.res.R
 import com.android.systemui.scene.domain.startable.sceneContainerStartable
 import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
@@ -166,21 +168,35 @@
         }
 
     @Test
-    fun isSideBySideSupported() =
+    fun isOneHandedModeSupported() =
         testScope.runTest {
-            val isSideBySideSupported by collectLastValue(underTest.isSideBySideSupported)
+            val isOneHandedModeSupported by collectLastValue(underTest.isOneHandedModeSupported)
             kosmos.fakeFeatureFlagsClassic.set(Flags.FULL_SCREEN_USER_SWITCHER, true)
+            kosmos.testableContext.orCreateTestableResources.addOverride(
+                R.bool.config_enableBouncerUserSwitcher,
+                true,
+            )
             kosmos.fakeAuthenticationRepository.setAuthenticationMethod(Pin)
-            assertThat(isSideBySideSupported).isTrue()
+            assertThat(isOneHandedModeSupported).isTrue()
             kosmos.fakeAuthenticationRepository.setAuthenticationMethod(Password)
-            assertThat(isSideBySideSupported).isTrue()
+            assertThat(isOneHandedModeSupported).isTrue()
 
             kosmos.fakeFeatureFlagsClassic.set(Flags.FULL_SCREEN_USER_SWITCHER, false)
+            kosmos.testableContext.orCreateTestableResources.addOverride(
+                R.bool.can_use_one_handed_bouncer,
+                true,
+            )
             kosmos.fakeAuthenticationRepository.setAuthenticationMethod(Pin)
-            assertThat(isSideBySideSupported).isTrue()
+            assertThat(isOneHandedModeSupported).isTrue()
 
             kosmos.fakeAuthenticationRepository.setAuthenticationMethod(Password)
-            assertThat(isSideBySideSupported).isFalse()
+            assertThat(isOneHandedModeSupported).isFalse()
+            kosmos.testableContext.orCreateTestableResources.removeOverride(
+                R.bool.config_enableBouncerUserSwitcher
+            )
+            kosmos.testableContext.orCreateTestableResources.removeOverride(
+                R.bool.can_use_one_handed_bouncer
+            )
         }
 
     @Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/brightness/ui/viewmodel/BrightnessSliderViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/brightness/ui/viewmodel/BrightnessSliderViewModelTest.kt
index 18f33e4..116b705 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/brightness/ui/viewmodel/BrightnessSliderViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/brightness/ui/viewmodel/BrightnessSliderViewModelTest.kt
@@ -30,16 +30,20 @@
 import com.android.systemui.common.shared.model.Text
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.haptics.slider.sliderHapticsViewModelFactory
-import com.android.systemui.kosmos.applicationCoroutineScope
 import com.android.systemui.kosmos.testScope
+import com.android.systemui.lifecycle.activateIn
 import com.android.systemui.res.R
+import com.android.systemui.settings.brightness.domain.interactor.brightnessMirrorShowingInteractor
 import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
+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
 
+@OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class BrightnessSliderViewModelTest : SysuiTestCase() {
@@ -49,15 +53,17 @@
 
     private val kosmos = testKosmos()
 
-    private val underTest =
+    private val underTest by lazy {
         with(kosmos) {
             BrightnessSliderViewModel(
                 screenBrightnessInteractor,
                 brightnessPolicyEnforcementInteractor,
-                applicationCoroutineScope,
                 sliderHapticsViewModelFactory,
+                brightnessMirrorShowingInteractor,
+                supportsMirroring = true,
             )
         }
+    }
 
     @Before
     fun setUp() {
@@ -65,18 +71,18 @@
             LinearBrightness(minBrightness),
             LinearBrightness(maxBrightness),
         )
+        underTest.activateIn(kosmos.testScope)
     }
 
     @Test
     fun brightnessChangeInRepository_changeInFlow() =
         with(kosmos) {
             testScope.runTest {
-                val gammaBrightness by collectLastValue(underTest.currentBrightness)
-
                 var brightness = 0.6f
                 fakeScreenBrightnessRepository.setBrightness(LinearBrightness(brightness))
+                runCurrent()
 
-                assertThat(gammaBrightness!!.value)
+                assertThat(underTest.currentBrightness.value)
                     .isEqualTo(
                         BrightnessUtils.convertLinearToGammaFloat(
                             brightness,
@@ -87,8 +93,9 @@
 
                 brightness = 0.2f
                 fakeScreenBrightnessRepository.setBrightness(LinearBrightness(brightness))
+                runCurrent()
 
-                assertThat(gammaBrightness!!.value)
+                assertThat(underTest.currentBrightness.value)
                     .isEqualTo(
                         BrightnessUtils.convertLinearToGammaFloat(
                             brightness,
@@ -117,7 +124,6 @@
             testScope.runTest {
                 val temporaryBrightness by
                     collectLastValue(fakeScreenBrightnessRepository.temporaryBrightness)
-                val brightness by collectLastValue(underTest.currentBrightness)
 
                 val newBrightness = underTest.maxBrightness.value / 3
                 val expectedTemporaryBrightness =
@@ -133,7 +139,7 @@
                 assertThat(temporaryBrightness!!.floatValue)
                     .isWithin(1e-5f)
                     .of(expectedTemporaryBrightness)
-                assertThat(brightness!!.value).isNotEqualTo(newBrightness)
+                assertThat(underTest.currentBrightness.value).isNotEqualTo(newBrightness)
             }
         }
 
@@ -141,14 +147,13 @@
     fun draggingStopped_currentBrightnessChanges() =
         with(kosmos) {
             testScope.runTest {
-                val brightness by collectLastValue(underTest.currentBrightness)
-
                 val newBrightness = underTest.maxBrightness.value / 3
                 val drag = Drag.Stopped(GammaBrightness(newBrightness))
 
                 underTest.onDrag(drag)
+                runCurrent()
 
-                assertThat(brightness!!.value).isEqualTo(newBrightness)
+                assertThat(underTest.currentBrightness.value).isEqualTo(newBrightness)
             }
         }
 
@@ -168,4 +173,40 @@
                 )
             )
     }
+
+    @Test
+    fun supportedMirror_mirrorShowingWhenDragging() =
+        with(kosmos) {
+            testScope.runTest {
+                val mirrorInInteractor by
+                    collectLastValue(brightnessMirrorShowingInteractor.isShowing)
+
+                underTest.setIsDragging(true)
+                assertThat(mirrorInInteractor).isEqualTo(true)
+                assertThat(underTest.showMirror).isEqualTo(true)
+
+                underTest.setIsDragging(false)
+                assertThat(mirrorInInteractor).isEqualTo(false)
+                assertThat(underTest.showMirror).isEqualTo(false)
+            }
+        }
+
+    @Test
+    fun unsupportedMirror_mirrorNeverShowing() =
+        with(kosmos) {
+            testScope.runTest {
+                val mirrorInInteractor by
+                    collectLastValue(brightnessMirrorShowingInteractor.isShowing)
+
+                val noMirrorViewModel = brightnessSliderViewModelFactory.create(false)
+
+                noMirrorViewModel.setIsDragging(true)
+                assertThat(mirrorInInteractor).isEqualTo(false)
+                assertThat(noMirrorViewModel.showMirror).isEqualTo(false)
+
+                noMirrorViewModel.setIsDragging(false)
+                assertThat(mirrorInInteractor).isEqualTo(false)
+                assertThat(noMirrorViewModel.showMirror).isEqualTo(false)
+            }
+        }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/broadcast/ActionReceiverTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/broadcast/ActionReceiverTest.kt
similarity index 98%
rename from packages/SystemUI/tests/src/com/android/systemui/broadcast/ActionReceiverTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/broadcast/ActionReceiverTest.kt
index b7ed27f..94d0dfe 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/broadcast/ActionReceiverTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/broadcast/ActionReceiverTest.kt
@@ -21,8 +21,8 @@
 import android.content.Intent
 import android.content.IntentFilter
 import android.os.UserHandle
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
-import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.broadcast.logging.BroadcastDispatcherLogger
@@ -51,7 +51,7 @@
 import java.lang.IllegalStateException
 import java.util.concurrent.Executor
 
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 @TestableLooper.RunWithLooper
 @SmallTest
 class ActionReceiverTest : SysuiTestCase() {
@@ -273,4 +273,4 @@
     fun testBroadcastWithWrongAction_throwsException() {
         actionReceiver.onReceive(mContext, Intent(ACTION2))
     }
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/viewmodel/ResizeableItemFrameViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/viewmodel/ResizeableItemFrameViewModelTest.kt
index 22b114c..7816d3b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/viewmodel/ResizeableItemFrameViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/viewmodel/ResizeableItemFrameViewModelTest.kt
@@ -366,6 +366,106 @@
             assertThat(bottomState.anchors.toList()).containsExactly(0 to 0f)
         }
 
+    @Test
+    fun testCanExpand_atTopPosition_withMultipleAnchors_returnsTrue() =
+        testScope.runTest {
+            val twoRowGrid = singleSpanGrid.copy(totalSpans = 2, currentSpan = 1, currentRow = 0)
+
+            updateGridLayout(twoRowGrid)
+            assertThat(underTest.canExpand()).isTrue()
+            assertThat(underTest.bottomDragState.anchors.toList())
+                .containsAtLeast(0 to 0f, 1 to 45f)
+        }
+
+    @Test
+    fun testCanExpand_atTopPosition_withSingleAnchors_returnsFalse() =
+        testScope.runTest {
+            val oneRowGrid = singleSpanGrid.copy(totalSpans = 1, currentSpan = 1, currentRow = 0)
+            updateGridLayout(oneRowGrid)
+            assertThat(underTest.canExpand()).isFalse()
+        }
+
+    @Test
+    fun testCanExpand_atBottomPosition_withMultipleAnchors_returnsTrue() =
+        testScope.runTest {
+            val twoRowGrid = singleSpanGrid.copy(totalSpans = 2, currentSpan = 1, currentRow = 1)
+            updateGridLayout(twoRowGrid)
+            assertThat(underTest.canExpand()).isTrue()
+            assertThat(underTest.topDragState.anchors.toList()).containsAtLeast(0 to 0f, -1 to -45f)
+        }
+
+    @Test
+    fun testCanShrink_atMinimumHeight_returnsFalse() =
+        testScope.runTest {
+            val oneRowGrid = singleSpanGrid.copy(totalSpans = 1, currentSpan = 1, currentRow = 0)
+            updateGridLayout(oneRowGrid)
+            assertThat(underTest.canShrink()).isFalse()
+        }
+
+    @Test
+    fun testCanShrink_atFullSize_checksBottomDragState() = runTestWithSnapshots {
+        val twoSpanGrid = singleSpanGrid.copy(totalSpans = 2, currentSpan = 2, currentRow = 0)
+        updateGridLayout(twoSpanGrid)
+
+        assertThat(underTest.canShrink()).isTrue()
+        assertThat(underTest.bottomDragState.anchors.toList()).containsAtLeast(0 to 0f, -1 to -45f)
+    }
+
+    @Test
+    fun testResizeByAccessibility_expandFromBottom_usesTopDragState() = runTestWithSnapshots {
+        val resizeInfo by collectLastValue(underTest.resizeInfo)
+
+        val twoSpanGrid = singleSpanGrid.copy(totalSpans = 2, currentSpan = 1, currentRow = 1)
+        updateGridLayout(twoSpanGrid)
+
+        underTest.expandToNextAnchor()
+
+        assertThat(resizeInfo).isEqualTo(ResizeInfo(1, DragHandle.TOP))
+    }
+
+    @Test
+    fun testResizeByAccessibility_expandFromTop_usesBottomDragState() = runTestWithSnapshots {
+        val resizeInfo by collectLastValue(underTest.resizeInfo)
+
+        val twoSpanGrid = singleSpanGrid.copy(totalSpans = 2, currentSpan = 1, currentRow = 0)
+        updateGridLayout(twoSpanGrid)
+
+        underTest.expandToNextAnchor()
+
+        assertThat(resizeInfo).isEqualTo(ResizeInfo(1, DragHandle.BOTTOM))
+    }
+
+    @Test
+    fun testResizeByAccessibility_shrinkFromFull_usesBottomDragState() = runTestWithSnapshots {
+        val resizeInfo by collectLastValue(underTest.resizeInfo)
+
+        val twoSpanGrid = singleSpanGrid.copy(totalSpans = 2, currentSpan = 2, currentRow = 0)
+        updateGridLayout(twoSpanGrid)
+
+        underTest.shrinkToNextAnchor()
+
+        assertThat(resizeInfo).isEqualTo(ResizeInfo(-1, DragHandle.BOTTOM))
+    }
+
+    @Test
+    fun testResizeByAccessibility_cannotResizeAtMinSize() = runTestWithSnapshots {
+        val resizeInfo by collectLastValue(underTest.resizeInfo)
+
+        // Set up grid at minimum size
+        val minSizeGrid =
+            singleSpanGrid.copy(
+                totalSpans = 2,
+                currentSpan = 1,
+                minHeightPx = singleSpanGrid.minHeightPx,
+                currentRow = 0,
+            )
+        updateGridLayout(minSizeGrid)
+
+        underTest.shrinkToNextAnchor()
+
+        assertThat(resizeInfo).isNull()
+    }
+
     @Test(expected = IllegalArgumentException::class)
     fun testIllegalState_maxHeightLessThanMinHeight() =
         testScope.runTest {
@@ -380,6 +480,24 @@
     fun testIllegalState_resizeMultipleZeroOrNegative() =
         testScope.runTest { updateGridLayout(singleSpanGrid.copy(resizeMultiple = 0)) }
 
+    @Test
+    fun testZeroHeights_cannotResize() = runTestWithSnapshots {
+        val zeroHeightGrid =
+            singleSpanGrid.copy(
+                totalSpans = 2,
+                currentSpan = 1,
+                currentRow = 0,
+                minHeightPx = 0,
+                maxHeightPx = 0,
+            )
+        updateGridLayout(zeroHeightGrid)
+
+        val topState = underTest.topDragState
+        val bottomState = underTest.bottomDragState
+        assertThat(topState.anchors.toList()).containsExactly(0 to 0f)
+        assertThat(bottomState.anchors.toList()).containsExactly(0 to 0f)
+    }
+
     private fun TestScope.updateGridLayout(gridLayout: GridLayout) {
         underTest.setGridLayoutInfo(
             verticalItemSpacingPx = gridLayout.verticalItemSpacingPx,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/compose/ComposeInitializerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/compose/ComposeInitializerTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/compose/ComposeInitializerTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/compose/ComposeInitializerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/display/data/repository/DisplayWindowPropertiesRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/DisplayWindowPropertiesRepositoryImplTest.kt
similarity index 88%
rename from packages/SystemUI/tests/src/com/android/systemui/display/data/repository/DisplayWindowPropertiesRepositoryImplTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/DisplayWindowPropertiesRepositoryImplTest.kt
index ff3186a..f68a1b5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/display/data/repository/DisplayWindowPropertiesRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/DisplayWindowPropertiesRepositoryImplTest.kt
@@ -19,15 +19,15 @@
 import android.content.testableContext
 import android.platform.test.annotations.EnableFlags
 import android.view.Display
+import android.view.layoutInflater
 import android.view.mockWindowManager
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.display.shared.model.DisplayWindowProperties
 import com.android.systemui.kosmos.applicationCoroutineScope
-import com.android.systemui.kosmos.testDispatcher
 import com.android.systemui.kosmos.testScope
-import com.android.systemui.kosmos.unconfinedTestDispatcher
+import com.android.systemui.kosmos.useUnconfinedTestDispatcher
 import com.android.systemui.statusbar.core.StatusBarConnectedDisplays
 import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
@@ -44,20 +44,24 @@
 @SmallTest
 class DisplayWindowPropertiesRepositoryImplTest : SysuiTestCase() {
 
-    private val kosmos = testKosmos().also { it.testDispatcher = it.unconfinedTestDispatcher }
+    private val kosmos = testKosmos().useUnconfinedTestDispatcher()
     private val fakeDisplayRepository = kosmos.displayRepository
     private val testScope = kosmos.testScope
 
     private val applicationContext = kosmos.testableContext
     private val applicationWindowManager = kosmos.mockWindowManager
+    private val applicationLayoutInflater = kosmos.layoutInflater
 
-    private val repo =
+    // Lazy so that @EnableFlags has time to run before this repo is instantiated
+    private val repo by lazy {
         DisplayWindowPropertiesRepositoryImpl(
             kosmos.applicationCoroutineScope,
             applicationContext,
             applicationWindowManager,
+            kosmos.layoutInflater,
             fakeDisplayRepository,
         )
+    }
 
     @Before
     fun start() {
@@ -82,6 +86,7 @@
                         windowType = WINDOW_TYPE_FOO,
                         context = applicationContext,
                         windowManager = applicationWindowManager,
+                        layoutInflater = applicationLayoutInflater,
                     )
                 )
         }
@@ -103,6 +108,14 @@
         }
 
     @Test
+    fun get_nonDefaultDisplayId_returnsNewLayoutInflater() =
+        testScope.runTest {
+            val displayContext = repo.get(NON_DEFAULT_DISPLAY_ID, WINDOW_TYPE_FOO)
+
+            assertThat(displayContext.layoutInflater).isNotSameInstanceAs(applicationLayoutInflater)
+        }
+
+    @Test
     fun get_multipleCallsForDefaultDisplay_returnsSameInstance() =
         testScope.runTest {
             val displayContext = repo.get(DEFAULT_DISPLAY_ID, WINDOW_TYPE_FOO)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/PerDisplayStoreImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/PerDisplayStoreImplTest.kt
index 1dd8ca9..6a0781b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/PerDisplayStoreImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/PerDisplayStoreImplTest.kt
@@ -20,9 +20,8 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.kosmos.testDispatcher
 import com.android.systemui.kosmos.testScope
-import com.android.systemui.kosmos.unconfinedTestDispatcher
+import com.android.systemui.kosmos.useUnconfinedTestDispatcher
 import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.runBlocking
@@ -37,7 +36,7 @@
 @SmallTest
 class PerDisplayStoreImplTest : SysuiTestCase() {
 
-    private val kosmos = testKosmos().also { it.testDispatcher = it.unconfinedTestDispatcher }
+    private val kosmos = testKosmos().useUnconfinedTestDispatcher()
     private val testScope = kosmos.testScope
     private val fakeDisplayRepository = kosmos.displayRepository
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt
index f5d2d42..8062358 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt
@@ -96,7 +96,7 @@
 import org.mockito.kotlin.mock
 import org.mockito.kotlin.spy
 import org.mockito.kotlin.times
-import org.mockito.kotlin.verifyZeroInteractions
+import org.mockito.kotlin.verifyNoMoreInteractions
 import org.mockito.kotlin.whenever
 
 @OptIn(ExperimentalCoroutinesApi::class)
@@ -453,7 +453,7 @@
 
         mMainExecutor.runAllReady()
 
-        verifyZeroInteractions(mTouchMonitor)
+        verifyNoMoreInteractions(mTouchMonitor)
 
         val captor = ArgumentCaptor.forClass(DreamOverlayStateController.Callback::class.java)
         verify(mStateController).addCallback(captor.capture())
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractorTest.kt
index 21679f9..2a6d29c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractorTest.kt
@@ -427,7 +427,7 @@
 
     @After
     fun clear() {
-        testScope.launch { tutorialSchedulerRepository.clearDataStore() }
+        testScope.launch { tutorialSchedulerRepository.clear() }
     }
 
     private suspend fun triggerMaxEducationSignals(gestureType: GestureType) {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/slider/SeekbarHapticPluginTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/slider/HapticSliderPluginTest.kt
similarity index 92%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/slider/SeekbarHapticPluginTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/slider/HapticSliderPluginTest.kt
index 587d3d9..0881010 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/slider/SeekbarHapticPluginTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/slider/HapticSliderPluginTest.kt
@@ -45,14 +45,14 @@
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 @OptIn(ExperimentalCoroutinesApi::class)
-class SeekbarHapticPluginTest : SysuiTestCase() {
+class HapticSliderPluginTest : SysuiTestCase() {
 
     private val kosmos = Kosmos()
 
     @Rule @JvmField val mMockitoRule: MockitoRule = MockitoJUnit.rule()
     @Mock private lateinit var vibratorHelper: VibratorHelper
     private val seekBar = SeekBar(mContext)
-    private lateinit var plugin: SeekbarHapticPlugin
+    private lateinit var plugin: HapticSliderPlugin
 
     @Before
     fun setup() {
@@ -95,7 +95,7 @@
         // GIVEN an onKeyDown that starts the wait and a program progress change that advances the
         // slider state to ARROW_HANDLE_MOVED_ONCE
         plugin.onKeyDown()
-        plugin.onProgressChanged(seekBar, 50, false)
+        plugin.onProgressChanged(50, false)
         testScheduler.runCurrent()
         assertThat(plugin.trackerState).isEqualTo(SliderState.ARROW_HANDLE_MOVED_ONCE)
 
@@ -112,7 +112,7 @@
         // GIVEN an onKeyDown that starts the wait and a program progress change that advances the
         // slider state to ARROW_HANDLE_MOVED_ONCE
         plugin.onKeyDown()
-        plugin.onProgressChanged(seekBar, 50, false)
+        plugin.onProgressChanged(50, false)
         testScheduler.runCurrent()
         assertThat(plugin.trackerState).isEqualTo(SliderState.ARROW_HANDLE_MOVED_ONCE)
 
@@ -142,7 +142,13 @@
         }
 
     private fun createPlugin() {
-        plugin = SeekbarHapticPlugin(vibratorHelper, kosmos.msdlPlayer, kosmos.fakeSystemClock)
+        plugin =
+            HapticSliderPlugin(
+                vibratorHelper,
+                kosmos.msdlPlayer,
+                kosmos.fakeSystemClock,
+                HapticSlider.SeekBar(seekBar),
+            )
     }
 
     companion object {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/slider/SliderHapticFeedbackProviderTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/slider/SliderHapticFeedbackProviderTest.kt
index 3467382..75fd566 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/slider/SliderHapticFeedbackProviderTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/slider/SliderHapticFeedbackProviderTest.kt
@@ -47,7 +47,7 @@
     private val kosmos = testKosmos()
     private val testScope = kosmos.testScope
 
-    private val config = SliderHapticFeedbackConfig()
+    private var config = SliderHapticFeedbackConfig()
 
     private val dragVelocityProvider = SliderDragVelocityProvider { config.maxVelocityToScale }
 
@@ -227,6 +227,72 @@
         }
 
     @Test
+    @DisableFlags(Flags.FLAG_MSDL_FEEDBACK)
+    fun playHapticAtProgress_forDiscreteSlider_playsTick() =
+        with(kosmos) {
+            config = SliderHapticFeedbackConfig(sliderStepSize = 0.2f)
+            sliderHapticFeedbackProvider =
+                SliderHapticFeedbackProvider(
+                    vibratorHelper,
+                    msdlPlayer,
+                    dragVelocityProvider,
+                    config,
+                    kosmos.fakeSystemClock,
+                )
+
+            // GIVEN max velocity and slider progress
+            val progress = 1f
+            val expectedScale =
+                sliderHapticFeedbackProvider.scaleOnDragTexture(config.maxVelocityToScale, progress)
+            val tick =
+                VibrationEffect.startComposition()
+                    .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, expectedScale)
+                    .compose()
+
+            // GIVEN system running for 1s
+            fakeSystemClock.advanceTime(1000)
+
+            // WHEN called to play haptics
+            sliderHapticFeedbackProvider.onProgress(progress)
+
+            // THEN the correct composition only plays once
+            assertEquals(expected = 1, vibratorHelper.timesVibratedWithEffect(tick))
+        }
+
+    @Test
+    @EnableFlags(Flags.FLAG_MSDL_FEEDBACK)
+    fun playHapticAtProgress_forDiscreteSlider_playsDiscreteSliderToken() =
+        with(kosmos) {
+            config = SliderHapticFeedbackConfig(sliderStepSize = 0.2f)
+            sliderHapticFeedbackProvider =
+                SliderHapticFeedbackProvider(
+                    vibratorHelper,
+                    msdlPlayer,
+                    dragVelocityProvider,
+                    config,
+                    kosmos.fakeSystemClock,
+                )
+
+            // GIVEN max velocity and slider progress
+            val progress = 1f
+            val expectedScale =
+                sliderHapticFeedbackProvider.scaleOnDragTexture(config.maxVelocityToScale, progress)
+            val expectedProperties =
+                InteractionProperties.DynamicVibrationScale(expectedScale, pipeliningAttributes)
+
+            // GIVEN system running for 1s
+            fakeSystemClock.advanceTime(1000)
+
+            // WHEN called to play haptics
+            sliderHapticFeedbackProvider.onProgress(progress)
+
+            // THEN the correct token plays once
+            assertThat(msdlPlayer.latestTokenPlayed).isEqualTo(MSDLToken.DRAG_INDICATOR_DISCRETE)
+            assertThat(msdlPlayer.latestPropertiesPlayed).isEqualTo(expectedProperties)
+            assertThat(msdlPlayer.getHistory().size).isEqualTo(1)
+        }
+
+    @Test
     @EnableFlags(Flags.FLAG_MSDL_FEEDBACK)
     fun playHapticAtProgress_onQuickSuccession_playsContinuousDragTokenOnce() =
         with(kosmos) {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/inputdevice/data/repository/TutorialSchedulerRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/inputdevice/data/repository/TutorialSchedulerRepositoryTest.kt
index 1d96c4d..8bb6962 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/inputdevice/data/repository/TutorialSchedulerRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/inputdevice/data/repository/TutorialSchedulerRepositoryTest.kt
@@ -47,13 +47,13 @@
             TutorialSchedulerRepository(
                 context,
                 testScope.backgroundScope,
-                "TutorialSchedulerRepositoryTest"
+                "TutorialSchedulerRepositoryTest",
             )
     }
 
     @After
     fun clear() {
-        testScope.launch { underTest.clearDataStore() }
+        testScope.launch { underTest.clear() }
     }
 
     @Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/inputdevice/tutorial/domain/interactor/TutorialNotificationCoordinatorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/inputdevice/tutorial/domain/interactor/TutorialNotificationCoordinatorTest.kt
index 38e4ae1..bcac086 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/inputdevice/tutorial/domain/interactor/TutorialNotificationCoordinatorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/inputdevice/tutorial/domain/interactor/TutorialNotificationCoordinatorTest.kt
@@ -26,10 +26,11 @@
 import com.android.systemui.inputdevice.tutorial.inputDeviceTutorialLogger
 import com.android.systemui.inputdevice.tutorial.ui.TutorialNotificationCoordinator
 import com.android.systemui.keyboard.data.repository.FakeKeyboardRepository
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.res.R
 import com.android.systemui.settings.userTracker
+import com.android.systemui.statusbar.commandline.commandRegistry
+import com.android.systemui.testKosmos
 import com.android.systemui.touchpad.data.repository.FakeTouchpadRepository
 import com.google.common.truth.Truth.assertThat
 import kotlin.time.Duration.Companion.hours
@@ -60,7 +61,7 @@
 class TutorialNotificationCoordinatorTest : SysuiTestCase() {
 
     private lateinit var underTest: TutorialNotificationCoordinator
-    private val kosmos = Kosmos()
+    private val kosmos = testKosmos()
     private val testScope = kosmos.testScope
     private val keyboardRepository = FakeKeyboardRepository()
     private val touchpadRepository = FakeTouchpadRepository()
@@ -85,6 +86,7 @@
                 touchpadRepository,
                 repository,
                 kosmos.inputDeviceTutorialLogger,
+                kosmos.commandRegistry,
             )
         underTest =
             TutorialNotificationCoordinator(
@@ -100,7 +102,7 @@
 
     @After
     fun clear() {
-        runBlocking { repository.clearDataStore() }
+        runBlocking { repository.clear() }
         dataStoreScope.cancel()
     }
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/inputdevice/tutorial/domain/interactor/TutorialSchedulerInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/inputdevice/tutorial/domain/interactor/TutorialSchedulerInteractorTest.kt
index b0ffc47..5df9b7b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/inputdevice/tutorial/domain/interactor/TutorialSchedulerInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/inputdevice/tutorial/domain/interactor/TutorialSchedulerInteractorTest.kt
@@ -24,8 +24,9 @@
 import com.android.systemui.inputdevice.tutorial.domain.interactor.TutorialSchedulerInteractor.TutorialType
 import com.android.systemui.inputdevice.tutorial.inputDeviceTutorialLogger
 import com.android.systemui.keyboard.data.repository.FakeKeyboardRepository
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testScope
+import com.android.systemui.statusbar.commandline.commandRegistry
+import com.android.systemui.testKosmos
 import com.android.systemui.touchpad.data.repository.FakeTouchpadRepository
 import com.google.common.truth.Truth.assertThat
 import kotlin.time.Duration.Companion.hours
@@ -49,7 +50,7 @@
 class TutorialSchedulerInteractorTest : SysuiTestCase() {
 
     private lateinit var underTest: TutorialSchedulerInteractor
-    private val kosmos = Kosmos()
+    private val kosmos = testKosmos()
     private val testScope = kosmos.testScope
     private lateinit var dataStoreScope: CoroutineScope
     private val keyboardRepository = FakeKeyboardRepository()
@@ -71,12 +72,13 @@
                 touchpadRepository,
                 schedulerRepository,
                 kosmos.inputDeviceTutorialLogger,
+                kosmos.commandRegistry,
             )
     }
 
     @After
     fun clear() {
-        runBlocking { schedulerRepository.clearDataStore() }
+        runBlocking { schedulerRepository.clear() }
         dataStoreScope.cancel()
     }
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLegacySettingSyncerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLegacySettingSyncerTest.kt
index 0145f17..4a422f0 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLegacySettingSyncerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLegacySettingSyncerTest.kt
@@ -23,8 +23,9 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.kosmos.unconfinedTestDispatcher
-import com.android.systemui.kosmos.unconfinedTestScope
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.kosmos.useUnconfinedTestDispatcher
 import com.android.systemui.res.R
 import com.android.systemui.settings.FakeUserTracker
 import com.android.systemui.shared.keyguard.shared.model.KeyguardQuickAffordanceSlots
@@ -33,7 +34,7 @@
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.mock
 import com.android.systemui.util.mockito.whenever
-import com.android.systemui.util.settings.unconfinedDispatcherFakeSettings
+import com.android.systemui.util.settings.fakeSettings
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.test.advanceUntilIdle
@@ -51,10 +52,10 @@
 @RunWith(AndroidJUnit4::class)
 class KeyguardQuickAffordanceLegacySettingSyncerTest : SysuiTestCase() {
 
-    private val kosmos = testKosmos()
-    private val testDispatcher = kosmos.unconfinedTestDispatcher
-    private val testScope = kosmos.unconfinedTestScope
-    private val settings = kosmos.unconfinedDispatcherFakeSettings
+    private val kosmos = testKosmos().useUnconfinedTestDispatcher()
+    private val testDispatcher = kosmos.testDispatcher
+    private val testScope = kosmos.testScope
+    private val settings = kosmos.fakeSettings
 
     @Mock private lateinit var sharedPrefs: FakeSharedPreferences
 
@@ -79,13 +80,7 @@
                 context = context,
                 userFileManager =
                     mock {
-                        whenever(
-                                getSharedPreferences(
-                                    anyString(),
-                                    anyInt(),
-                                    anyInt(),
-                                )
-                            )
+                        whenever(getSharedPreferences(anyString(), anyInt(), anyInt()))
                             .thenReturn(FakeSharedPreferences())
                     },
                 userTracker = FakeUserTracker(),
@@ -109,17 +104,14 @@
         testScope.runTest {
             val job = underTest.startSyncing()
 
-            settings.putInt(
-                Settings.Secure.LOCKSCREEN_SHOW_CONTROLS,
-                1,
-            )
+            settings.putInt(Settings.Secure.LOCKSCREEN_SHOW_CONTROLS, 1)
 
             assertThat(
                     selectionManager
                         .getSelections()
                         .getOrDefault(
                             KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START,
-                            emptyList()
+                            emptyList(),
                         )
                 )
                 .contains(BuiltInKeyguardQuickAffordanceKeys.HOME_CONTROLS)
@@ -132,21 +124,15 @@
         testScope.runTest {
             val job = underTest.startSyncing()
 
-            settings.putInt(
-                Settings.Secure.LOCKSCREEN_SHOW_CONTROLS,
-                1,
-            )
-            settings.putInt(
-                Settings.Secure.LOCKSCREEN_SHOW_CONTROLS,
-                0,
-            )
+            settings.putInt(Settings.Secure.LOCKSCREEN_SHOW_CONTROLS, 1)
+            settings.putInt(Settings.Secure.LOCKSCREEN_SHOW_CONTROLS, 0)
 
             assertThat(
                     selectionManager
                         .getSelections()
                         .getOrDefault(
                             KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START,
-                            emptyList()
+                            emptyList(),
                         )
                 )
                 .doesNotContain(BuiltInKeyguardQuickAffordanceKeys.HOME_CONTROLS)
@@ -161,7 +147,7 @@
 
             selectionManager.setSelections(
                 KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_END,
-                listOf(BuiltInKeyguardQuickAffordanceKeys.QUICK_ACCESS_WALLET)
+                listOf(BuiltInKeyguardQuickAffordanceKeys.QUICK_ACCESS_WALLET),
             )
 
             advanceUntilIdle()
@@ -177,11 +163,11 @@
 
             selectionManager.setSelections(
                 KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_END,
-                listOf(BuiltInKeyguardQuickAffordanceKeys.QUICK_ACCESS_WALLET)
+                listOf(BuiltInKeyguardQuickAffordanceKeys.QUICK_ACCESS_WALLET),
             )
             selectionManager.setSelections(
                 KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_END,
-                emptyList()
+                emptyList(),
             )
 
             assertThat(settings.getInt(Settings.Secure.LOCKSCREEN_SHOW_WALLET)).isEqualTo(0)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardBlueprintRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/KeyguardBlueprintRepositoryTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardBlueprintRepositoryTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/KeyguardBlueprintRepositoryTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractorTest.kt
index d97909a1..e149687 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractorTest.kt
@@ -19,25 +19,24 @@
 
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
+import com.android.app.tracing.coroutines.launchTraced
 import com.android.compose.animation.scene.ObservableTransitionState
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.authentication.data.repository.fakeAuthenticationRepository
 import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
 import com.android.systemui.bouncer.data.repository.fakeKeyguardBouncerRepository
-import com.android.systemui.bouncer.domain.interactor.alternateBouncerInteractor
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.deviceentry.data.repository.fakeDeviceEntryRepository
 import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor
-import com.android.systemui.deviceentry.domain.interactor.deviceUnlockedInteractor
 import com.android.systemui.flags.EnableSceneContainer
 import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFingerprintAuthRepository
 import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
+import com.android.systemui.keyguard.data.repository.keyguardRepository
 import com.android.systemui.keyguard.shared.model.DismissAction
 import com.android.systemui.keyguard.shared.model.KeyguardDone
 import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.power.data.repository.fakePowerRepository
-import com.android.systemui.power.domain.interactor.powerInteractor
 import com.android.systemui.power.shared.model.WakeSleepReason
 import com.android.systemui.power.shared.model.WakefulnessState
 import com.android.systemui.scene.data.repository.Idle
@@ -45,12 +44,12 @@
 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.shade.domain.interactor.shadeInteractor
 import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.test.TestScope
 import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
 import org.junit.Before
@@ -76,25 +75,13 @@
         MockitoAnnotations.initMocks(this)
 
         dismissInteractor = kosmos.keyguardDismissInteractor
-        underTest =
-            KeyguardDismissActionInteractor(
-                repository = keyguardRepository,
-                transitionInteractor = kosmos.keyguardTransitionInteractor,
-                dismissInteractor = dismissInteractor,
-                applicationScope = testScope.backgroundScope,
-                deviceUnlockedInteractor = { kosmos.deviceUnlockedInteractor },
-                powerInteractor = kosmos.powerInteractor,
-                alternateBouncerInteractor = kosmos.alternateBouncerInteractor,
-                shadeInteractor = { kosmos.shadeInteractor },
-                keyguardInteractor = { kosmos.keyguardInteractor },
-                sceneInteractor = { kosmos.sceneInteractor },
-            )
+        underTest = kosmos.keyguardDismissActionInteractor
     }
 
     @Test
     fun updateDismissAction_onRepoChange() =
         testScope.runTest {
-            val dismissAction by collectLastValue(underTest.dismissAction)
+            val dismissAction by collectLastValue(keyguardRepository.dismissAction)
 
             val newDismissAction =
                 DismissAction.RunImmediately(
@@ -152,11 +139,16 @@
         }
 
     @Test
-    fun executeDismissAction_dismissKeyguardRequestWithImmediateDismissAction_biometricAuthed() =
+    fun dismissActionExecuted_ImmediateDismissAction_biometricAuthed() =
         testScope.runTest {
-            val executeDismissAction by collectLastValue(underTest.executeDismissAction)
+            val keyguardDoneTiming by collectLastValue(kosmos.keyguardRepository.keyguardDone)
+            var wasDismissActionInvoked = false
+            startInteractor()
 
-            val onDismissAction = { KeyguardDone.IMMEDIATE }
+            val onDismissAction = {
+                wasDismissActionInvoked = true
+                KeyguardDone.IMMEDIATE
+            }
             keyguardRepository.setDismissAction(
                 DismissAction.RunImmediately(
                     onDismissAction = onDismissAction,
@@ -166,16 +158,48 @@
                 )
             )
             kosmos.fakeKeyguardBouncerRepository.setKeyguardAuthenticatedBiometrics(true)
-            assertThat(executeDismissAction).isEqualTo(onDismissAction)
+            runCurrent()
+
+            assertThat(wasDismissActionInvoked).isTrue()
+            assertThat(keyguardDoneTiming).isEqualTo(KeyguardDone.IMMEDIATE)
+            assertThat(keyguardRepository.dismissAction.value).isEqualTo(DismissAction.None)
         }
 
     @Test
-    fun executeDismissAction_dismissKeyguardRequestWithoutImmediateDismissAction() =
+    fun dismissActionExecuted_LaterKeyguardDoneTimingIsStored_biometricAuthed() =
         testScope.runTest {
-            val executeDismissAction by collectLastValue(underTest.executeDismissAction)
+            val keyguardDoneTiming by collectLastValue(kosmos.keyguardRepository.keyguardDone)
+            var wasDismissActionInvoked = false
+            startInteractor()
+
+            val onDismissAction = {
+                wasDismissActionInvoked = true
+                KeyguardDone.LATER
+            }
+            keyguardRepository.setDismissAction(
+                DismissAction.RunImmediately(
+                    onDismissAction = onDismissAction,
+                    onCancelAction = {},
+                    message = "message",
+                    willAnimateOnLockscreen = true,
+                )
+            )
+            kosmos.fakeKeyguardBouncerRepository.setKeyguardAuthenticatedBiometrics(true)
+            runCurrent()
+
+            assertThat(wasDismissActionInvoked).isTrue()
+            assertThat(keyguardDoneTiming).isEqualTo(KeyguardDone.LATER)
+            assertThat(keyguardRepository.dismissAction.value).isEqualTo(DismissAction.None)
+        }
+
+    @Test
+    fun dismissActionExecuted_WithoutImmediateDismissAction() =
+        testScope.runTest {
+            var wasDismissActionInvoked = false
+            startInteractor()
 
             // WHEN a keyguard action will run after the keyguard is gone
-            val onDismissAction = {}
+            val onDismissAction = { wasDismissActionInvoked = true }
             keyguardRepository.setDismissAction(
                 DismissAction.RunAfterKeyguardGone(
                     dismissAction = onDismissAction,
@@ -184,33 +208,39 @@
                     willAnimateOnLockscreen = true,
                 )
             )
-            assertThat(executeDismissAction).isNull()
+            assertThat(wasDismissActionInvoked).isFalse()
 
             kosmos.fakeDeviceEntryFingerprintAuthRepository.setAuthenticationStatus(
                 SuccessFingerprintAuthenticationStatus(0, true)
             )
             kosmos.setSceneTransition(Idle(Scenes.Gone))
             kosmos.sceneInteractor.changeScene(Scenes.Gone, "")
+            runCurrent()
 
-            assertThat(executeDismissAction).isNotNull()
+            assertThat(wasDismissActionInvoked).isTrue()
+            assertThat(keyguardRepository.dismissAction.value).isEqualTo(DismissAction.None)
         }
 
     @Test
     fun resetDismissAction() =
         testScope.runTest {
             kosmos.setSceneTransition(Idle(Scenes.Bouncer))
-            val resetDismissAction by collectLastValue(underTest.resetDismissAction)
+            var wasOnCancelInvoked = false
+            startInteractor()
             keyguardRepository.setDismissAction(
                 DismissAction.RunAfterKeyguardGone(
                     dismissAction = {},
-                    onCancelAction = {},
+                    onCancelAction = { wasOnCancelInvoked = true },
                     message = "message",
                     willAnimateOnLockscreen = true,
                 )
             )
-            assertThat(resetDismissAction).isNull()
+            assertThat(wasOnCancelInvoked).isFalse()
             kosmos.setSceneTransition(Idle(Scenes.Lockscreen))
-            assertThat(resetDismissAction).isEqualTo(Unit)
+            runCurrent()
+
+            assertThat(wasOnCancelInvoked).isTrue()
+            assertThat(keyguardRepository.dismissAction.value).isEqualTo(DismissAction.None)
         }
 
     @Test
@@ -220,21 +250,25 @@
             kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
                 AuthenticationMethodModel.None
             )
-            val resetDismissAction by collectLastValue(underTest.resetDismissAction)
-            keyguardRepository.setDismissAction(
+            var wasOnCancelInvoked = false
+
+            val dismissAction =
                 DismissAction.RunAfterKeyguardGone(
                     dismissAction = {},
-                    onCancelAction = {},
+                    onCancelAction = { wasOnCancelInvoked = true },
                     message = "message",
                     willAnimateOnLockscreen = true,
                 )
-            )
-            assertThat(resetDismissAction).isNull()
+            keyguardRepository.setDismissAction(dismissAction)
+            assertThat(wasOnCancelInvoked).isFalse()
 
             kosmos.setSceneTransition(
                 Transition(from = Scenes.Bouncer, to = Scenes.Shade, progress = flowOf(1f))
             )
-            assertThat(resetDismissAction).isNull()
+            runCurrent()
+
+            assertThat(wasOnCancelInvoked).isFalse()
+            assertThat(keyguardRepository.dismissAction.value).isEqualTo(dismissAction)
         }
 
     @Test
@@ -244,29 +278,34 @@
             kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
                 AuthenticationMethodModel.None
             )
-            val resetDismissAction by collectLastValue(underTest.resetDismissAction)
+            var wasOnCancelInvoked = false
+            startInteractor()
+
             keyguardRepository.setDismissAction(
                 DismissAction.RunAfterKeyguardGone(
                     dismissAction = {},
-                    onCancelAction = {},
+                    onCancelAction = { wasOnCancelInvoked = true },
                     message = "message",
                     willAnimateOnLockscreen = true,
                 )
             )
-            assertThat(resetDismissAction).isNull()
+            assertThat(wasOnCancelInvoked).isFalse()
             kosmos.fakePowerRepository.updateWakefulness(
                 rawState = WakefulnessState.ASLEEP,
                 lastWakeReason = WakeSleepReason.POWER_BUTTON,
                 lastSleepReason = WakeSleepReason.TIMEOUT,
                 powerButtonLaunchGestureTriggered = false,
             )
-            assertThat(resetDismissAction).isEqualTo(Unit)
+            runCurrent()
+
+            assertThat(wasOnCancelInvoked).isTrue()
+            assertThat(keyguardRepository.dismissAction.value).isEqualTo(DismissAction.None)
         }
 
     @Test
     fun setDismissAction_callsCancelRunnableOnPreviousDismissAction() =
         testScope.runTest {
-            val dismissAction by collectLastValue(underTest.dismissAction)
+            val dismissAction by collectLastValue(keyguardRepository.dismissAction)
             var previousDismissActionCancelCalled = false
             keyguardRepository.setDismissAction(
                 DismissAction.RunImmediately(
@@ -294,27 +333,6 @@
         }
 
     @Test
-    fun handleDismissAction() =
-        testScope.runTest {
-            val dismissAction by collectLastValue(underTest.dismissAction)
-            underTest.handleDismissAction()
-            assertThat(dismissAction).isEqualTo(DismissAction.None)
-        }
-
-    @Test
-    fun setKeyguardDone() =
-        testScope.runTest {
-            val keyguardDoneTiming by collectLastValue(dismissInteractor.keyguardDone)
-            runCurrent()
-
-            underTest.setKeyguardDone(KeyguardDone.LATER)
-            assertThat(keyguardDoneTiming).isEqualTo(KeyguardDone.LATER)
-
-            underTest.setKeyguardDone(KeyguardDone.IMMEDIATE)
-            assertThat(keyguardDoneTiming).isEqualTo(KeyguardDone.IMMEDIATE)
-        }
-
-    @Test
     @EnableSceneContainer
     fun dismissAction_executesBeforeItsReset_sceneContainerOn_swipeAuth_fromQsScene() =
         testScope.runTest {
@@ -324,11 +342,11 @@
                 MutableStateFlow<ObservableTransitionState>(
                     ObservableTransitionState.Idle(currentScene!!)
                 )
+            startInteractor()
+
             kosmos.sceneInteractor.setTransitionState(transitionState)
-            val executeDismissAction by collectLastValue(underTest.executeDismissAction)
-            val resetDismissAction by collectLastValue(underTest.resetDismissAction)
-            assertThat(executeDismissAction).isNull()
-            assertThat(resetDismissAction).isNull()
+            var wasDismissActionInvoked = false
+            var wasCancelActionInvoked = false
             kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
                 AuthenticationMethodModel.None
             )
@@ -338,20 +356,23 @@
             transitionState.value = ObservableTransitionState.Idle(Scenes.QuickSettings)
             assertThat(currentScene).isEqualTo(Scenes.QuickSettings)
 
-            assertThat(executeDismissAction).isNull()
-            assertThat(resetDismissAction).isNull()
+            assertThat(wasDismissActionInvoked).isFalse()
+            assertThat(wasCancelActionInvoked).isFalse()
 
             val dismissAction =
                 DismissAction.RunImmediately(
-                    onDismissAction = { KeyguardDone.LATER },
-                    onCancelAction = {},
+                    onDismissAction = {
+                        wasDismissActionInvoked = true
+                        KeyguardDone.LATER
+                    },
+                    onCancelAction = { wasCancelActionInvoked = true },
                     message = "message",
                     willAnimateOnLockscreen = true,
                 )
             underTest.setDismissAction(dismissAction)
-            // Should still be null because the transition to Gone has not yet happened.
-            assertThat(executeDismissAction).isNull()
-            assertThat(resetDismissAction).isNull()
+            // Should still not be run because the transition to Gone has not yet happened.
+            assertThat(wasDismissActionInvoked).isFalse()
+            assertThat(wasCancelActionInvoked).isFalse()
 
             transitionState.value =
                 ObservableTransitionState.Transition.ChangeScene(
@@ -366,8 +387,8 @@
                     isInPreviewStage = flowOf(false),
                 )
             runCurrent()
-            assertThat(executeDismissAction).isNull()
-            assertThat(resetDismissAction).isNull()
+            assertThat(wasDismissActionInvoked).isFalse()
+            assertThat(wasCancelActionInvoked).isFalse()
 
             transitionState.value =
                 ObservableTransitionState.Transition.ChangeScene(
@@ -384,7 +405,17 @@
             kosmos.sceneInteractor.changeScene(Scenes.Gone, "")
             assertThat(currentScene).isEqualTo(Scenes.Gone)
             runCurrent()
-            assertThat(executeDismissAction).isNotNull()
-            assertThat(resetDismissAction).isNull()
+
+            assertThat(wasDismissActionInvoked).isTrue()
+            assertThat(wasCancelActionInvoked).isFalse()
         }
+
+    private fun TestScope.startInteractor() {
+        testScope.backgroundScope.launchTraced(
+            "KeyguardDismissActionInteractorTest#startInteractor"
+        ) {
+            underTest.activate()
+        }
+        runCurrent()
+    }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardLockWhileAwakeInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardLockWhileAwakeInteractorTest.kt
new file mode 100644
index 0000000..bd26e42
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardLockWhileAwakeInteractorTest.kt
@@ -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.systemui.keyguard.domain.interactor
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectValues
+import com.android.systemui.keyguard.data.repository.biometricSettingsRepository
+import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlin.test.Test
+import kotlin.test.assertEquals
+import kotlinx.coroutines.test.advanceTimeBy
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+@kotlinx.coroutines.ExperimentalCoroutinesApi
+class KeyguardLockWhileAwakeInteractorTest : SysuiTestCase() {
+    private val kosmos = testKosmos()
+    private val testScope = kosmos.testScope
+    private lateinit var underTest: KeyguardLockWhileAwakeInteractor
+
+    @Before
+    fun setup() {
+        underTest = kosmos.keyguardLockWhileAwakeInteractor
+    }
+
+    @Test
+    fun emitsMultipleTimeoutEvents() =
+        testScope.runTest {
+            val values by collectValues(underTest.lockWhileAwakeEvents)
+
+            underTest.onKeyguardServiceDoKeyguardTimeout(options = null)
+            runCurrent()
+
+            assertThat(values)
+                .containsExactly(LockWhileAwakeReason.KEYGUARD_TIMEOUT_WHILE_SCREEN_ON)
+
+            advanceTimeBy(1000)
+            underTest.onKeyguardServiceDoKeyguardTimeout(options = null)
+            runCurrent()
+
+            assertThat(values)
+                .containsExactly(
+                    LockWhileAwakeReason.KEYGUARD_TIMEOUT_WHILE_SCREEN_ON,
+                    LockWhileAwakeReason.KEYGUARD_TIMEOUT_WHILE_SCREEN_ON,
+                )
+        }
+
+    @Test
+    fun emitsWhenKeyguardEnabled_onlyIfShowingWhenDisabled() =
+        testScope.runTest {
+            val values by collectValues(underTest.lockWhileAwakeEvents)
+
+            kosmos.biometricSettingsRepository.setIsUserInLockdown(false)
+            runCurrent()
+
+            kosmos.keyguardEnabledInteractor.notifyKeyguardEnabled(false)
+            runCurrent()
+
+            assertEquals(0, values.size)
+
+            kosmos.keyguardEnabledInteractor.notifyKeyguardEnabled(true)
+            runCurrent()
+
+            assertThat(values).containsExactly(LockWhileAwakeReason.KEYGUARD_REENABLED)
+
+            kosmos.fakeKeyguardTransitionRepository.sendTransitionSteps(
+                from = KeyguardState.LOCKSCREEN,
+                to = KeyguardState.GONE,
+                testScope = testScope,
+            )
+            kosmos.keyguardEnabledInteractor.notifyKeyguardEnabled(false)
+            kosmos.keyguardEnabledInteractor.notifyKeyguardEnabled(true)
+            runCurrent()
+
+            assertThat(values).containsExactly(LockWhileAwakeReason.KEYGUARD_REENABLED)
+        }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt
index eef4c3d..32fa160 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt
@@ -79,13 +79,10 @@
 import platform.test.runner.parameterized.Parameters
 
 @OptIn(ExperimentalCoroutinesApi::class)
-@FlakyTest(
-    bugId = 292574995,
-    detail = "on certain architectures all permutations with startActivity=true is causing failures"
-)
 @SmallTest
 @RunWith(ParameterizedAndroidJunit4::class)
 @DisableSceneContainer
+@FlakyTest(bugId = 292574995, detail = "NullPointer on MockMakerTypeMockability.mockable()")
 class KeyguardQuickAffordanceInteractorParameterizedTest : SysuiTestCase() {
 
     companion object {
@@ -93,11 +90,7 @@
         private val DRAWABLE =
             mock<Icon> {
                 whenever(this.contentDescription)
-                    .thenReturn(
-                        ContentDescription.Resource(
-                            res = CONTENT_DESCRIPTION_RESOURCE_ID,
-                        )
-                    )
+                    .thenReturn(ContentDescription.Resource(res = CONTENT_DESCRIPTION_RESOURCE_ID))
             }
         private const val CONTENT_DESCRIPTION_RESOURCE_ID = 1337
 
@@ -273,13 +266,7 @@
                 context = context,
                 userFileManager =
                     mock<UserFileManager>().apply {
-                        whenever(
-                                getSharedPreferences(
-                                    anyString(),
-                                    anyInt(),
-                                    anyInt(),
-                                )
-                            )
+                        whenever(getSharedPreferences(anyString(), anyInt(), anyInt()))
                             .thenReturn(FakeSharedPreferences())
                     },
                 userTracker = userTracker,
@@ -316,9 +303,7 @@
         underTest =
             KeyguardQuickAffordanceInteractor(
                 keyguardInteractor =
-                    KeyguardInteractorFactory.create(
-                            featureFlags = featureFlags,
-                        )
+                    KeyguardInteractorFactory.create(featureFlags = featureFlags)
                         .keyguardInteractor,
                 shadeInteractor = kosmos.shadeInteractor,
                 lockPatternUtils = lockPatternUtils,
@@ -350,9 +335,7 @@
 
             homeControls.setState(
                 lockScreenState =
-                    KeyguardQuickAffordanceConfig.LockScreenState.Visible(
-                        icon = DRAWABLE,
-                    )
+                    KeyguardQuickAffordanceConfig.LockScreenState.Visible(icon = DRAWABLE)
             )
             homeControls.onTriggeredResult =
                 if (startActivity) {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
index f4d2ea0..c8a1648 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
@@ -560,7 +560,9 @@
                 .startedTransition(
                     to = KeyguardState.LOCKSCREEN,
                     from = KeyguardState.GONE,
-                    ownerName = "FromGoneTransitionInteractor",
+                    ownerName =
+                        "FromGoneTransitionInteractor" +
+                            "(keyguard interactor says keyguard is showing)",
                     animatorAssertion = { it.isNotNull() },
                 )
 
@@ -640,7 +642,9 @@
                 .startedTransition(
                     to = KeyguardState.GLANCEABLE_HUB,
                     from = KeyguardState.GONE,
-                    ownerName = FromGoneTransitionInteractor::class.simpleName,
+                    ownerName =
+                        FromGoneTransitionInteractor::class.simpleName +
+                            "(keyguard interactor says keyguard is showing)",
                     animatorAssertion = { it.isNotNull() },
                 )
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/scenetransition/LockscreenSceneTransitionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/scenetransition/LockscreenSceneTransitionInteractorTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/scenetransition/LockscreenSceneTransitionInteractorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/scenetransition/LockscreenSceneTransitionInteractorTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSectionTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSectionTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSectionTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSectionTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardSmartspaceViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardSmartspaceViewModelTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardSmartspaceViewModelTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardSmartspaceViewModelTest.kt
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionManagerRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionManagerRepositoryTest.kt
index 785d5a8..02825a5 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionManagerRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionManagerRepositoryTest.kt
@@ -21,10 +21,13 @@
 import android.os.Binder
 import android.os.Handler
 import android.os.UserHandle
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
 import android.view.ContentRecordingSession
 import android.view.Display
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
+import com.android.systemui.Flags.FLAG_STATUS_BAR_SHOW_AUDIO_ONLY_PROJECTION_CHIP
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.kosmos.applicationCoroutineScope
@@ -74,7 +77,8 @@
         }
 
     @Test
-    fun mediaProjectionState_onStart_emitsNotProjecting() =
+    @DisableFlags(FLAG_STATUS_BAR_SHOW_AUDIO_ONLY_PROJECTION_CHIP)
+    fun mediaProjectionState_onStart_flagOff_emitsNotProjecting() =
         testScope.runTest {
             val state by collectLastValue(repo.mediaProjectionState)
 
@@ -84,6 +88,35 @@
         }
 
     @Test
+    @EnableFlags(FLAG_STATUS_BAR_SHOW_AUDIO_ONLY_PROJECTION_CHIP)
+    fun mediaProjectionState_onStart_flagOn_emitsProjectingNoScreen() =
+        testScope.runTest {
+            val state by collectLastValue(repo.mediaProjectionState)
+
+            fakeMediaProjectionManager.dispatchOnStart()
+
+            assertThat(state).isInstanceOf(MediaProjectionState.Projecting.NoScreen::class.java)
+        }
+
+    @Test
+    @EnableFlags(FLAG_STATUS_BAR_SHOW_AUDIO_ONLY_PROJECTION_CHIP)
+    fun mediaProjectionState_noScreen_hasHostPackage() =
+        testScope.runTest {
+            val state by collectLastValue(repo.mediaProjectionState)
+
+            val info =
+                MediaProjectionInfo(
+                    /* packageName= */ "com.media.projection.repository.test",
+                    /* handle= */ UserHandle.getUserHandleForUid(UserHandle.myUserId()),
+                    /* launchCookie = */ null,
+                )
+            fakeMediaProjectionManager.dispatchOnStart(info)
+
+            assertThat((state as MediaProjectionState.Projecting).hostPackage)
+                .isEqualTo("com.media.projection.repository.test")
+        }
+
+    @Test
     fun mediaProjectionState_onStop_emitsNotProjecting() =
         testScope.runTest {
             val state by collectLastValue(repo.mediaProjectionState)
@@ -212,7 +245,7 @@
                 )
             fakeMediaProjectionManager.dispatchOnSessionSet(
                 info = info,
-                session = ContentRecordingSession.createTaskSession(token.asBinder())
+                session = ContentRecordingSession.createTaskSession(token.asBinder()),
             )
 
             assertThat((state as MediaProjectionState.Projecting.SingleTask).hostPackage)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/power/PowerNotificationWarningsTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/power/PowerNotificationWarningsTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/power/PowerNotificationWarningsTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/power/PowerNotificationWarningsTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyConfigFlagsTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/privacy/PrivacyConfigFlagsTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyConfigFlagsTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/privacy/PrivacyConfigFlagsTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogV2Test.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/privacy/PrivacyDialogV2Test.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogV2Test.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/privacy/PrivacyDialogV2Test.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/FgsManagerControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/FgsManagerControllerTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/qs/FgsManagerControllerTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/qs/FgsManagerControllerTest.java
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java
index 527aeaa..83f95ea 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java
@@ -28,6 +28,8 @@
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyFloat;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.doAnswer;
@@ -604,6 +606,46 @@
         assertThat(mPagedTileLayoutListening).isFalse();
     }
 
+    @Test
+    public void reAttach_configurationChangePending_triggersConfigurationListener() {
+        mController.onViewDetached();
+
+        when(mQSPanel.hadConfigurationChangeWhileDetached()).thenReturn(true);
+        clearInvocations(mQSLogger);
+
+        mController.onViewAttached();
+
+        verify(mQSLogger).logOnConfigurationChanged(
+                anyInt(),
+                anyInt(),
+                anyBoolean(),
+                anyBoolean(),
+                anyInt(),
+                anyInt(),
+                anyString()
+        );
+    }
+
+    @Test
+    public void reAttach_noConfigurationChangePending_doesntTriggerConfigurationListener() {
+        mController.onViewDetached();
+
+        when(mQSPanel.hadConfigurationChangeWhileDetached()).thenReturn(false);
+        clearInvocations(mQSLogger);
+
+        mController.onViewAttached();
+
+        verify(mQSLogger, never()).logOnConfigurationChanged(
+                anyInt(),
+                anyInt(),
+                anyBoolean(),
+                anyBoolean(),
+                anyInt(),
+                anyInt(),
+                anyString()
+        );
+    }
+
 
     private boolean usingMediaPlayer() {
         return !SceneContainerFlag.isEnabled();
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/composefragment/viewmodel/AbstractQSFragmentComposeViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/composefragment/viewmodel/AbstractQSFragmentComposeViewModelTest.kt
index d96e664..2e2894d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/composefragment/viewmodel/AbstractQSFragmentComposeViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/composefragment/viewmodel/AbstractQSFragmentComposeViewModelTest.kt
@@ -19,6 +19,7 @@
 import androidx.lifecycle.Lifecycle
 import androidx.lifecycle.lifecycleScope
 import androidx.lifecycle.testing.TestLifecycleOwner
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.kosmos.testDispatcher
 import com.android.systemui.kosmos.testScope
@@ -33,7 +34,9 @@
 import kotlinx.coroutines.test.setMain
 import org.junit.After
 import org.junit.Before
+import org.junit.runner.RunWith
 
+@RunWith(AndroidJUnit4::class)
 @OptIn(ExperimentalCoroutinesApi::class)
 abstract class AbstractQSFragmentComposeViewModelTest : SysuiTestCase() {
     protected val kosmos = testKosmos()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileRequestDialogEventLoggerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/external/TileRequestDialogEventLoggerTest.kt
similarity index 97%
rename from packages/SystemUI/tests/src/com/android/systemui/qs/external/TileRequestDialogEventLoggerTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/qs/external/TileRequestDialogEventLoggerTest.kt
index 64796f1..12bd5af 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileRequestDialogEventLoggerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/external/TileRequestDialogEventLoggerTest.kt
@@ -17,8 +17,8 @@
 package com.android.systemui.qs.external
 
 import android.app.StatusBarManager
-import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.internal.logging.InstanceId
 import com.android.internal.logging.UiEventLogger
@@ -31,7 +31,7 @@
 import org.junit.runner.RunWith
 
 @SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 @TestableLooper.RunWithLooper
 class TileRequestDialogEventLoggerTest : SysuiTestCase() {
 
@@ -138,4 +138,4 @@
         assertThat(packageName).isEqualTo(PACKAGE_NAME)
         assertThat(this.instanceId).isEqualTo(instanceId)
     }
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingDataInteractorTest.kt
index 2194c75..f3fc0ab 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingDataInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingDataInteractorTest.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.qs.tiles.impl.irecording
 
+import android.os.Handler
 import android.os.UserHandle
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
@@ -28,6 +29,7 @@
 import com.android.systemui.recordissue.IssueRecordingState
 import com.android.systemui.settings.fakeUserFileManager
 import com.android.systemui.settings.userTracker
+import com.android.systemui.util.settings.fakeGlobalSettings
 import com.google.common.truth.Truth
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.flowOf
@@ -51,7 +53,14 @@
 
     @Before
     fun setup() {
-        state = IssueRecordingState(userTracker, userFileManager)
+        state =
+            IssueRecordingState(
+                userTracker,
+                userFileManager,
+                Handler.getMain(),
+                mContext.contentResolver,
+                kosmos.fakeGlobalSettings,
+            )
         underTest = IssueRecordingDataInteractor(state, kosmos.testScope.testScheduler)
     }
 
@@ -67,10 +76,12 @@
             Truth.assertThat(data?.isRecording).isFalse()
 
             state.isRecording = true
+            state.onRecordingChangeListener.onChange(true)
             runCurrent()
             Truth.assertThat(data?.isRecording).isTrue()
 
             state.isRecording = false
+            state.onRecordingChangeListener.onChange(true)
             runCurrent()
             Truth.assertThat(data?.isRecording).isFalse()
         }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingUserActionInteractorTest.kt
index 99d2da67..9c2be89 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingUserActionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingUserActionInteractorTest.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.qs.tiles.impl.irecording
 
+import android.os.Handler
 import android.os.UserHandle
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
@@ -38,6 +39,7 @@
 import com.android.systemui.settings.userTracker
 import com.android.systemui.statusbar.phone.KeyguardDismissUtil
 import com.android.systemui.statusbar.policy.keyguardStateController
+import com.android.systemui.util.settings.fakeGlobalSettings
 import com.google.common.truth.Truth
 import kotlinx.coroutines.test.runTest
 import org.junit.Before
@@ -83,7 +85,13 @@
             underTest =
                 IssueRecordingUserActionInteractor(
                     testDispatcher,
-                    IssueRecordingState(userTracker, userFileManager),
+                    IssueRecordingState(
+                        userTracker,
+                        userFileManager,
+                        Handler.getMain(),
+                        mContext.contentResolver,
+                        kosmos.fakeGlobalSettings,
+                    ),
                     KeyguardDismissUtil(
                         keyguardStateController,
                         statusBarStateController,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayActionsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayActionsViewModelTest.kt
index fd1c043..c3a777c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayActionsViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayActionsViewModelTest.kt
@@ -28,6 +28,7 @@
 import com.android.systemui.flags.EnableSceneContainer
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.lifecycle.activateIn
+import com.android.systemui.qs.panels.ui.viewmodel.editModeViewModel
 import com.android.systemui.scene.shared.model.Overlays
 import com.android.systemui.scene.ui.viewmodel.SceneContainerEdge
 import com.android.systemui.testKosmos
@@ -62,8 +63,7 @@
     fun back_notEditing_hidesShade() =
         testScope.runTest {
             val actions by collectLastValue(underTest.actions)
-            val isEditing by
-                collectLastValue(kosmos.quickSettingsContainerViewModel.editModeViewModel.isEditing)
+            val isEditing by collectLastValue(kosmos.editModeViewModel.isEditing)
             underTest.activateIn(this)
             assertThat(isEditing).isFalse()
 
@@ -77,7 +77,7 @@
             val actions by collectLastValue(underTest.actions)
             underTest.activateIn(this)
 
-            kosmos.quickSettingsContainerViewModel.editModeViewModel.startEditing()
+            kosmos.editModeViewModel.startEditing()
 
             assertThat(actions?.get(Back)).isNull()
         }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeUserActionsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeUserActionsViewModelTest.kt
deleted file mode 100644
index 32772d2..0000000
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeUserActionsViewModelTest.kt
+++ /dev/null
@@ -1,183 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.qs.ui.viewmodel
-
-import android.testing.TestableLooper
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.SmallTest
-import com.android.compose.animation.scene.Back
-import com.android.compose.animation.scene.Swipe
-import com.android.compose.animation.scene.UserActionResult
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.authentication.data.repository.fakeAuthenticationRepository
-import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
-import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.deviceentry.data.repository.fakeDeviceEntryRepository
-import com.android.systemui.deviceentry.domain.interactor.deviceUnlockedInteractor
-import com.android.systemui.flags.EnableSceneContainer
-import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFingerprintAuthRepository
-import com.android.systemui.keyguard.domain.interactor.keyguardEnabledInteractor
-import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus
-import com.android.systemui.kosmos.testScope
-import com.android.systemui.lifecycle.activateIn
-import com.android.systemui.qs.panels.ui.viewmodel.editModeViewModel
-import com.android.systemui.scene.domain.interactor.sceneInteractor
-import com.android.systemui.scene.domain.resolver.homeSceneFamilyResolver
-import com.android.systemui.scene.shared.model.SceneFamilies
-import com.android.systemui.scene.shared.model.Scenes
-import com.android.systemui.testKosmos
-import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.test.TestScope
-import kotlinx.coroutines.test.runCurrent
-import kotlinx.coroutines.test.runTest
-import org.junit.Test
-import org.junit.runner.RunWith
-
-@OptIn(ExperimentalCoroutinesApi::class)
-@SmallTest
-@RunWith(AndroidJUnit4::class)
-@TestableLooper.RunWithLooper
-@EnableSceneContainer
-class QuickSettingsShadeUserActionsViewModelTest : SysuiTestCase() {
-
-    private val kosmos = testKosmos()
-    private val testScope = kosmos.testScope
-    private val sceneInteractor = kosmos.sceneInteractor
-    private val deviceUnlockedInteractor = kosmos.deviceUnlockedInteractor
-
-    private val underTest by lazy { kosmos.quickSettingsShadeUserActionsViewModel }
-
-    @Test
-    fun upTransitionSceneKey_deviceLocked_lockscreen() =
-        testScope.runTest {
-            underTest.activateIn(this)
-            val actions by collectLastValue(underTest.actions)
-            val homeScene by collectLastValue(kosmos.homeSceneFamilyResolver.resolvedScene)
-            lockDevice()
-
-            assertThat((actions?.get(Swipe.Up) as? UserActionResult.ChangeScene)?.toScene)
-                .isEqualTo(SceneFamilies.Home)
-            assertThat(actions?.get(Swipe.Down)).isNull()
-            assertThat(homeScene).isEqualTo(Scenes.Lockscreen)
-        }
-
-    @Test
-    fun upTransitionSceneKey_deviceLocked_keyguardDisabled_gone() =
-        testScope.runTest {
-            underTest.activateIn(this)
-            val actions by collectLastValue(underTest.actions)
-            val homeScene by collectLastValue(kosmos.homeSceneFamilyResolver.resolvedScene)
-            lockDevice()
-            kosmos.keyguardEnabledInteractor.notifyKeyguardEnabled(false)
-
-            assertThat((actions?.get(Swipe.Up) as? UserActionResult.ChangeScene)?.toScene)
-                .isEqualTo(SceneFamilies.Home)
-            assertThat(homeScene).isEqualTo(Scenes.Gone)
-        }
-
-    @Test
-    fun upTransitionSceneKey_deviceUnlocked_gone() =
-        testScope.runTest {
-            underTest.activateIn(this)
-            val actions by collectLastValue(underTest.actions)
-            val homeScene by collectLastValue(kosmos.homeSceneFamilyResolver.resolvedScene)
-            lockDevice()
-            unlockDevice()
-
-            assertThat((actions?.get(Swipe.Up) as? UserActionResult.ChangeScene)?.toScene)
-                .isEqualTo(SceneFamilies.Home)
-            assertThat(actions?.get(Swipe.Down)).isNull()
-            assertThat(homeScene).isEqualTo(Scenes.Gone)
-        }
-
-    @Test
-    fun upTransitionSceneKey_authMethodSwipe_lockscreenNotDismissed_goesToLockscreen() =
-        testScope.runTest {
-            underTest.activateIn(this)
-            val actions by collectLastValue(underTest.actions)
-            val homeScene by collectLastValue(kosmos.homeSceneFamilyResolver.resolvedScene)
-            kosmos.fakeDeviceEntryRepository.setLockscreenEnabled(true)
-            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
-                AuthenticationMethodModel.None
-            )
-            sceneInteractor.changeScene(Scenes.Lockscreen, "reason")
-
-            assertThat((actions?.get(Swipe.Up) as? UserActionResult.ChangeScene)?.toScene)
-                .isEqualTo(SceneFamilies.Home)
-            assertThat(homeScene).isEqualTo(Scenes.Lockscreen)
-        }
-
-    @Test
-    fun upTransitionSceneKey_authMethodSwipe_lockscreenDismissed_goesToGone() =
-        testScope.runTest {
-            underTest.activateIn(this)
-            val actions by collectLastValue(underTest.actions)
-            val homeScene by collectLastValue(kosmos.homeSceneFamilyResolver.resolvedScene)
-            kosmos.fakeDeviceEntryRepository.setLockscreenEnabled(true)
-            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
-                AuthenticationMethodModel.None
-            )
-            runCurrent()
-            sceneInteractor.changeScene(Scenes.Gone, "reason")
-
-            assertThat((actions?.get(Swipe.Up) as? UserActionResult.ChangeScene)?.toScene)
-                .isEqualTo(SceneFamilies.Home)
-            assertThat(homeScene).isEqualTo(Scenes.Gone)
-        }
-
-    @Test
-    fun backTransitionSceneKey_notEditing_Home() =
-        testScope.runTest {
-            underTest.activateIn(this)
-            val actions by collectLastValue(underTest.actions)
-
-            assertThat((actions?.get(Back) as? UserActionResult.ChangeScene)?.toScene)
-                .isEqualTo(SceneFamilies.Home)
-        }
-
-    @Test
-    fun backTransition_editing_noDestination() =
-        testScope.runTest {
-            underTest.activateIn(this)
-            val actions by collectLastValue(underTest.actions)
-            kosmos.editModeViewModel.startEditing()
-
-            assertThat(actions!!).isNotEmpty()
-            assertThat(actions?.get(Back)).isNull()
-        }
-
-    private fun TestScope.lockDevice() {
-        val deviceUnlockStatus by collectLastValue(deviceUnlockedInteractor.deviceUnlockStatus)
-
-        kosmos.fakeAuthenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
-        assertThat(deviceUnlockStatus?.isUnlocked).isFalse()
-        sceneInteractor.changeScene(Scenes.Lockscreen, "reason")
-        runCurrent()
-    }
-
-    private fun TestScope.unlockDevice() {
-        val deviceUnlockStatus by collectLastValue(deviceUnlockedInteractor.deviceUnlockStatus)
-
-        kosmos.fakeDeviceEntryFingerprintAuthRepository.setAuthenticationStatus(
-            SuccessFingerprintAuthenticationStatus(0, true)
-        )
-        assertThat(deviceUnlockStatus?.isUnlocked).isTrue()
-        sceneInteractor.changeScene(Scenes.Gone, "reason")
-        runCurrent()
-    }
-}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/recordissue/IssueRecordingServiceSessionTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/recordissue/IssueRecordingServiceSessionTest.kt
index aceaab8..f2e658d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/recordissue/IssueRecordingServiceSessionTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/recordissue/IssueRecordingServiceSessionTest.kt
@@ -19,6 +19,7 @@
 import android.app.IActivityManager
 import android.app.NotificationManager
 import android.net.Uri
+import android.os.Handler
 import android.os.UserHandle
 import android.testing.TestableLooper
 import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -33,6 +34,7 @@
 import com.android.systemui.settings.UserContextProvider
 import com.android.systemui.settings.userFileManager
 import com.android.systemui.settings.userTracker
+import com.android.systemui.util.settings.fakeGlobalSettings
 import com.android.traceur.TraceConfig
 import com.google.common.truth.Truth
 import org.junit.Before
@@ -55,7 +57,13 @@
     private val dialogTransitionAnimator: DialogTransitionAnimator = kosmos.dialogTransitionAnimator
     private lateinit var traceurConnection: TraceurConnection
     private val issueRecordingState =
-        IssueRecordingState(kosmos.userTracker, kosmos.userFileManager)
+        IssueRecordingState(
+            kosmos.userTracker,
+            kosmos.userFileManager,
+            Handler.getMain(),
+            mContext.contentResolver,
+            kosmos.fakeGlobalSettings,
+        )
 
     private val iActivityManager = mock<IActivityManager>()
     private val notificationManager = mock<NotificationManager>()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/recordissue/IssueRecordingStateTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/recordissue/IssueRecordingStateTest.kt
index 4ab3c7b..83bdcd2 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/recordissue/IssueRecordingStateTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/recordissue/IssueRecordingStateTest.kt
@@ -16,6 +16,8 @@
 
 package com.android.systemui.recordissue
 
+import android.content.ContentResolver
+import android.os.Handler
 import android.testing.TestableLooper
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
@@ -23,11 +25,15 @@
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.settings.userFileManager
 import com.android.systemui.settings.userTracker
+import com.android.systemui.util.settings.fakeGlobalSettings
 import com.google.common.truth.Truth
-import java.util.concurrent.CountDownLatch
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.any
+import org.mockito.kotlin.verify
 
 @SmallTest
 @RunWith(AndroidJUnit4::class)
@@ -36,10 +42,19 @@
 
     private val kosmos = Kosmos()
     private lateinit var underTest: IssueRecordingState
+    @Mock private lateinit var resolver: ContentResolver
 
     @Before
     fun setup() {
-        underTest = IssueRecordingState(kosmos.userTracker, kosmos.userFileManager)
+        MockitoAnnotations.initMocks(this)
+        underTest =
+            IssueRecordingState(
+                kosmos.userTracker,
+                kosmos.userFileManager,
+                Handler.getMain(),
+                resolver,
+                kosmos.fakeGlobalSettings,
+            )
     }
 
     @Test
@@ -47,7 +62,14 @@
         val expected = true
 
         underTest.takeBugreport = expected
-        underTest = IssueRecordingState(kosmos.userTracker, kosmos.userFileManager)
+        underTest =
+            IssueRecordingState(
+                kosmos.userTracker,
+                kosmos.userFileManager,
+                Handler.getMain(),
+                resolver,
+                kosmos.fakeGlobalSettings,
+            )
 
         Truth.assertThat(underTest.takeBugreport).isEqualTo(expected)
     }
@@ -57,7 +79,14 @@
         val expected = true
 
         underTest.recordScreen = expected
-        underTest = IssueRecordingState(kosmos.userTracker, kosmos.userFileManager)
+        underTest =
+            IssueRecordingState(
+                kosmos.userTracker,
+                kosmos.userFileManager,
+                Handler.getMain(),
+                resolver,
+                kosmos.fakeGlobalSettings,
+            )
 
         Truth.assertThat(underTest.recordScreen).isEqualTo(expected)
     }
@@ -65,7 +94,14 @@
     @Test
     fun hasUserApprovedScreenRecording_isTrue_afterBeingMarkedAsCompleted() {
         underTest.markUserApprovalForScreenRecording()
-        underTest = IssueRecordingState(kosmos.userTracker, kosmos.userFileManager)
+        underTest =
+            IssueRecordingState(
+                kosmos.userTracker,
+                kosmos.userFileManager,
+                Handler.getMain(),
+                resolver,
+                kosmos.fakeGlobalSettings,
+            )
 
         Truth.assertThat(underTest.hasUserApprovedScreenRecording).isEqualTo(true)
     }
@@ -75,35 +111,35 @@
         val expected = setOf("a", "b", "c")
 
         underTest.tagTitles = expected
-        underTest = IssueRecordingState(kosmos.userTracker, kosmos.userFileManager)
+        underTest =
+            IssueRecordingState(
+                kosmos.userTracker,
+                kosmos.userFileManager,
+                Handler.getMain(),
+                resolver,
+                kosmos.fakeGlobalSettings,
+            )
 
         Truth.assertThat(underTest.tagTitles).isEqualTo(expected)
     }
 
     @Test
-    fun isRecording_callsListeners_onTheValueChanging() {
-        val count = CountDownLatch(1)
-        val listener = Runnable { count.countDown() }
+    fun addListener_registersContentObserver_ifListOfListenersIsNotEmpty() {
+        val listener = Runnable { /* No-op */ }
 
         underTest.addListener(listener)
-        underTest.isRecording = true
 
-        Truth.assertThat(count.count).isEqualTo(0)
+        verify(resolver).registerContentObserver(any(), any(), any())
     }
 
     @Test
-    fun isRecording_callsOnlyListeners_whoHaveNotBeenRemoved() {
-        val count1 = CountDownLatch(1)
-        val count2 = CountDownLatch(1)
-        val listener1 = Runnable { count1.countDown() }
-        val listener2 = Runnable { count2.countDown() }
+    fun removeListener_unRegistersContentObserver_ifListOfListenersIsEmpty() {
+        val listener = Runnable { /* No-op */ }
 
-        underTest.addListener(listener1)
-        underTest.removeListener(listener1)
-        underTest.addListener(listener2)
-        underTest.isRecording = true
+        underTest.addListener(listener)
+        underTest.removeListener(listener)
 
-        Truth.assertThat(count1.count).isEqualTo(1)
-        Truth.assertThat(count2.count).isEqualTo(0)
+        verify(resolver).registerContentObserver(any(), any(), any())
+        verify(resolver).unregisterContentObserver(any())
     }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
index d2bf9b88..9db2014 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
@@ -332,7 +332,7 @@
             assertWithMessage("Bouncer action button not visible")
                 .that(bouncerActionButton)
                 .isNotNull()
-            bouncerActionButton?.onClick?.invoke()
+            kosmos.bouncerSceneContentViewModel.onActionButtonClicked(bouncerActionButton!!)
             runCurrent()
 
             // TODO(b/369765704): Assert that an activity was started once we use ActivityStarter.
@@ -354,7 +354,7 @@
             assertWithMessage("Bouncer action button not visible during call")
                 .that(bouncerActionButton)
                 .isNotNull()
-            bouncerActionButton?.onClick?.invoke()
+            kosmos.bouncerSceneContentViewModel.onActionButtonClicked(bouncerActionButton!!)
             runCurrent()
 
             verify(kosmos.mockTelecomManager).showInCallScreen(any())
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 3850891..4995920 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
@@ -651,6 +651,7 @@
         }
 
     @Test
+    @DisableFlags(Flags.FLAG_TRANSITION_RACE_CONDITION)
     fun switchToAOD_whenAvailable_whenDeviceSleepsLocked() =
         testScope.runTest {
             kosmos.lockscreenSceneTransitionInteractor.start()
@@ -680,6 +681,7 @@
         }
 
     @Test
+    @DisableFlags(Flags.FLAG_TRANSITION_RACE_CONDITION)
     fun switchToDozing_whenAodUnavailable_whenDeviceSleepsLocked() =
         testScope.runTest {
             kosmos.lockscreenSceneTransitionInteractor.start()
@@ -701,6 +703,56 @@
         }
 
     @Test
+    @EnableFlags(Flags.FLAG_TRANSITION_RACE_CONDITION)
+    fun switchToAOD_whenAvailable_whenDeviceSleepsLocked_transitionFlagEnabled() =
+        testScope.runTest {
+            kosmos.lockscreenSceneTransitionInteractor.start()
+            val asleepState by collectLastValue(kosmos.keyguardInteractor.asleepKeyguardState)
+            val transitionState =
+                prepareState(isDeviceUnlocked = false, initialSceneKey = Scenes.Shade)
+            kosmos.keyguardRepository.setAodAvailable(true)
+            runCurrent()
+            assertThat(asleepState).isEqualTo(KeyguardState.AOD)
+            underTest.start()
+            powerInteractor.setAsleepForTest()
+            runCurrent()
+            transitionState.value =
+                ObservableTransitionState.Transition(
+                    fromScene = Scenes.Shade,
+                    toScene = Scenes.Lockscreen,
+                    currentScene = flowOf(Scenes.Lockscreen),
+                    progress = flowOf(0.5f),
+                    isInitiatedByUserInput = true,
+                    isUserInputOngoing = flowOf(false),
+                )
+            runCurrent()
+
+            assertThat(kosmos.keyguardTransitionRepository.currentTransitionInfo.to)
+                .isEqualTo(KeyguardState.AOD)
+        }
+
+    @Test
+    @EnableFlags(Flags.FLAG_TRANSITION_RACE_CONDITION)
+    fun switchToDozing_whenAodUnavailable_whenDeviceSleepsLocked_transitionFlagEnabled() =
+        testScope.runTest {
+            kosmos.lockscreenSceneTransitionInteractor.start()
+            val asleepState by collectLastValue(kosmos.keyguardInteractor.asleepKeyguardState)
+            val transitionState =
+                prepareState(isDeviceUnlocked = false, initialSceneKey = Scenes.Shade)
+            kosmos.keyguardRepository.setAodAvailable(false)
+            runCurrent()
+            assertThat(asleepState).isEqualTo(KeyguardState.DOZING)
+            underTest.start()
+            powerInteractor.setAsleepForTest()
+            runCurrent()
+            transitionState.value = Transition(from = Scenes.Shade, to = Scenes.Lockscreen)
+            runCurrent()
+
+            assertThat(kosmos.keyguardTransitionRepository.currentTransitionInfo.to)
+                .isEqualTo(KeyguardState.DOZING)
+        }
+
+    @Test
     fun switchToGoneWhenDoubleTapPowerGestureIsTriggeredFromGone() =
         testScope.runTest {
             val currentSceneKey by collectLastValue(sceneInteractor.currentScene)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/screenrecord/RecordingServiceTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/screenrecord/RecordingServiceTest.java
index bff3903..a6a1d4a 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/screenrecord/RecordingServiceTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/screenrecord/RecordingServiceTest.java
@@ -310,6 +310,13 @@
         verify(mNotificationManager).cancelAsUser(any(), anyInt(), any());
     }
 
+    @Test
+        public void testSecondaryDisplayRecording() throws IOException {
+        Intent startIntent =
+                RecordingService.getStartIntent(mContext, 0, 0, false, 200, null);
+        assertEquals(startIntent.getIntExtra("extra_displayId", -1), 200);
+    }
+
     private void assertUpdateState(boolean state) {
         // Then the state is set to not recording, and we cancel the notification
         // non SYSTEM user doesn't have the reference to the correct controller,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogDelegateTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogDelegateTest.kt
index 7dae5cc..534c12c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogDelegateTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogDelegateTest.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.screenrecord
 
 import android.content.Intent
+import android.hardware.display.DisplayManager
 import android.os.UserHandle
 import android.testing.TestableLooper
 import android.view.View
@@ -89,6 +90,7 @@
                 mediaProjectionMetricsLogger,
                 systemUIDialogFactory,
                 context,
+                context.getSystemService(DisplayManager::class.java)!!,
             )
         dialog = delegate.createDialog()
     }
@@ -161,7 +163,7 @@
 
         assertExtraPassedToAppSelector(
             extraKey = MediaProjectionAppSelectorActivity.EXTRA_HOST_APP_UID,
-            value = TEST_HOST_UID
+            value = TEST_HOST_UID,
         )
     }
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/AnnouncementResolverTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/AnnouncementResolverTest.kt
index 2e8498a..6580e3b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/AnnouncementResolverTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/AnnouncementResolverTest.kt
@@ -16,7 +16,7 @@
 
 package com.android.systemui.screenshot
 
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.screenshot.data.repository.profileTypeRepository
 import com.android.systemui.screenshot.policy.TestUserIds
@@ -30,7 +30,7 @@
 import org.junit.runner.RunWith
 import org.mockito.Mockito.mock
 
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 class AnnouncementResolverTest {
     private val kosmos = Kosmos()
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/DefaultScreenshotActionsProviderTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/DefaultScreenshotActionsProviderTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/screenshot/DefaultScreenshotActionsProviderTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/DefaultScreenshotActionsProviderTest.kt
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/scroll/ScrollCaptureFrameworkSmokeTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/scroll/ScrollCaptureFrameworkSmokeTest.java
index 5699cfc..a5d239a 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/scroll/ScrollCaptureFrameworkSmokeTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/scroll/ScrollCaptureFrameworkSmokeTest.java
@@ -22,7 +22,6 @@
 
 import android.content.Intent;
 import android.os.RemoteException;
-import android.testing.AndroidTestingRunner;
 import android.util.Log;
 import android.view.Display;
 import android.view.IScrollCaptureResponseListener;
@@ -30,6 +29,7 @@
 import android.view.ScrollCaptureResponse;
 import android.view.WindowManagerGlobal;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 import androidx.test.platform.app.InstrumentationRegistry;
 
@@ -45,7 +45,7 @@
 /**
  * Tests the of internal framework Scroll Capture API from SystemUI.
  */
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 @SmallTest
 @Ignore
 public class ScrollCaptureFrameworkSmokeTest extends SysuiTestCase {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/settings/brightness/BrightnessSliderControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/settings/brightness/BrightnessSliderControllerTest.kt
index 080f46f..637a12c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/settings/brightness/BrightnessSliderControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/settings/brightness/BrightnessSliderControllerTest.kt
@@ -16,15 +16,16 @@
 
 package com.android.systemui.settings.brightness
 
-import android.testing.AndroidTestingRunner
 import android.view.MotionEvent
 import android.widget.SeekBar
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.internal.logging.testing.UiEventLoggerFake
 import com.android.settingslib.RestrictedLockUtils
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.classifier.FalsingManagerFake
-import com.android.systemui.haptics.slider.SeekbarHapticPlugin
+import com.android.systemui.haptics.slider.HapticSlider
+import com.android.systemui.haptics.slider.HapticSliderPlugin
 import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.statusbar.VibratorHelper
 import com.android.systemui.statusbar.policy.BrightnessMirrorController
@@ -51,7 +52,7 @@
 import org.mockito.MockitoAnnotations
 
 @SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 class BrightnessSliderControllerTest : SysuiTestCase() {
 
     @Mock private lateinit var brightnessSliderView: BrightnessSliderView
@@ -86,7 +87,12 @@
                 brightnessSliderView,
                 mFalsingManager,
                 uiEventLogger,
-                SeekbarHapticPlugin(vibratorHelper, msdlPlayer, systemClock),
+                HapticSliderPlugin(
+                    vibratorHelper,
+                    msdlPlayer,
+                    systemClock,
+                    HapticSlider.SeekBar(seekBar),
+                ),
                 activityStarter,
             )
         mController.init()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
index 89ad699..0ff2a4a 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
@@ -128,6 +128,7 @@
 import com.android.systemui.qs.QSFragmentLegacy;
 import com.android.systemui.res.R;
 import com.android.systemui.screenrecord.RecordingController;
+import com.android.systemui.settings.brightness.domain.interactor.BrightnessMirrorShowingInteractor;
 import com.android.systemui.shade.data.repository.FakeShadeRepository;
 import com.android.systemui.shade.data.repository.ShadeAnimationRepository;
 import com.android.systemui.shade.data.repository.ShadeRepository;
@@ -373,6 +374,9 @@
     protected ShadeRepository mShadeRepository;
     protected FakeMSDLPlayer mMSDLPlayer = mKosmos.getMsdlPlayer();
 
+    protected BrightnessMirrorShowingInteractor mBrightnessMirrorShowingInteractor =
+            mKosmos.getBrightnessMirrorShowingInteractor();
+
     protected final FalsingManagerFake mFalsingManager = new FalsingManagerFake();
     protected final Optional<SysUIUnfoldComponent> mSysUIUnfoldComponent = Optional.empty();
     protected final DisplayMetrics mDisplayMetrics = new DisplayMetrics();
@@ -752,7 +756,8 @@
                 mPowerInteractor,
                 mKeyguardClockPositionAlgorithm,
                 mNaturalScrollingSettingObserver,
-                mMSDLPlayer);
+                mMSDLPlayer,
+                mBrightnessMirrorShowingInteractor);
         mNotificationPanelViewController.initDependencies(
                 mCentralSurfaces,
                 null,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
index 47eebf6..59d0d70 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
@@ -51,7 +51,12 @@
 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
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.qs.flags.QSComposeFragment
 import com.android.systemui.res.R
+import com.android.systemui.settings.brightness.data.repository.BrightnessMirrorShowingRepository
+import com.android.systemui.settings.brightness.domain.interactor.BrightnessMirrorShowingInteractor
 import com.android.systemui.shade.NotificationShadeWindowView.InteractionEventHandler
 import com.android.systemui.shade.domain.interactor.PanelExpansionInteractor
 import com.android.systemui.statusbar.DragDownHelper
@@ -70,6 +75,7 @@
 import com.android.systemui.statusbar.phone.DozeServiceHost
 import com.android.systemui.statusbar.phone.PhoneStatusBarViewController
 import com.android.systemui.statusbar.window.StatusBarWindowStateController
+import com.android.systemui.testKosmos
 import com.android.systemui.unfold.SysUIUnfoldComponent
 import com.android.systemui.unfold.UnfoldTransitionProgressProvider
 import com.android.systemui.user.domain.interactor.SelectedUserInteractor
@@ -78,11 +84,15 @@
 import com.android.systemui.util.time.FakeSystemClock
 import com.google.common.truth.Truth.assertThat
 import java.util.Optional
+import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.emptyFlow
 import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.resetMain
+import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
+import kotlinx.coroutines.test.setMain
 import org.junit.Assert.assertEquals
 import org.junit.Before
 import org.junit.Test
@@ -98,6 +108,7 @@
 import org.mockito.Mockito.verify
 import org.mockito.Mockito.`when` as whenever
 import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.clearInvocations
 import platform.test.runner.parameterized.ParameterizedAndroidJunit4
 import platform.test.runner.parameterized.Parameters
 
@@ -107,6 +118,8 @@
 @RunWithLooper(setAsMainLooper = true)
 class NotificationShadeWindowViewControllerTest(flags: FlagsParameterization) : SysuiTestCase() {
 
+    val kosmos = testKosmos()
+
     @Mock private lateinit var view: NotificationShadeWindowView
     @Mock private lateinit var sysuiStatusBarStateController: SysuiStatusBarStateController
     @Mock private lateinit var centralSurfaces: CentralSurfaces
@@ -148,6 +161,10 @@
     private val notificationLaunchAnimationInteractor =
         NotificationLaunchAnimationInteractor(notificationLaunchAnimationRepository)
 
+    private val brightnessMirrorShowingRepository = BrightnessMirrorShowingRepository()
+    private val brightnessMirrorShowingInteractor =
+        BrightnessMirrorShowingInteractor(brightnessMirrorShowingRepository)
+
     private lateinit var falsingCollector: FalsingCollectorFake
     private lateinit var fakeClock: FakeSystemClock
     private lateinit var interactionEventHandlerCaptor: ArgumentCaptor<InteractionEventHandler>
@@ -181,8 +198,9 @@
         featureFlagsClassic.set(SPLIT_SHADE_SUBPIXEL_OPTIMIZATION, true)
         mSetFlagsRule.enableFlags(Flags.FLAG_REVAMPED_BOUNCER_MESSAGES)
 
-        testScope = TestScope()
+        testScope = kosmos.testScope
         testableLooper = TestableLooper.get(this)
+
         falsingCollector = FalsingCollectorFake()
         fakeClock = FakeSystemClock()
         underTest =
@@ -221,6 +239,7 @@
                 alternateBouncerInteractor,
                 mock(BouncerViewBinder::class.java),
                 mock(ConfigurationForwarder::class.java),
+                brightnessMirrorShowingInteractor,
             )
         underTest.setupExpandedStatusBar()
         underTest.setDragDownHelper(dragDownHelper)
@@ -597,6 +616,39 @@
         verify(dragDownHelper).stopDragging()
     }
 
+    @Test
+    @EnableFlags(QSComposeFragment.FLAG_NAME)
+    fun mirrorShowing_depthControllerSet() =
+        testScope.runTest {
+            try {
+                Dispatchers.setMain(kosmos.testDispatcher)
+
+                // Simulate attaching the view so flow collection starts.
+                whenever(view.viewTreeObserver).thenReturn(mock(ViewTreeObserver::class.java))
+                val onAttachStateChangeListenerArgumentCaptor =
+                    ArgumentCaptor.forClass(View.OnAttachStateChangeListener::class.java)
+                verify(view, atLeast(1))
+                    .addOnAttachStateChangeListener(
+                        onAttachStateChangeListenerArgumentCaptor.capture()
+                    )
+                for (listener in onAttachStateChangeListenerArgumentCaptor.allValues) {
+                    listener.onViewAttachedToWindow(view)
+                }
+                testableLooper.processAllMessages()
+                clearInvocations(notificationShadeDepthController)
+
+                brightnessMirrorShowingInteractor.setMirrorShowing(true)
+                runCurrent()
+                verify(notificationShadeDepthController).brightnessMirrorVisible = true
+
+                brightnessMirrorShowingInteractor.setMirrorShowing(false)
+                runCurrent()
+                verify(notificationShadeDepthController).brightnessMirrorVisible = false
+            } finally {
+                Dispatchers.resetMain()
+            }
+        }
+
     companion object {
         private val DOWN_EVENT = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0)
         private val MOVE_EVENT = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_MOVE, 0f, 0f, 0)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
index 1c196c0..9b91fc7 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
@@ -42,6 +42,8 @@
 import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING
 import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
 import com.android.systemui.res.R
+import com.android.systemui.settings.brightness.data.repository.BrightnessMirrorShowingRepository
+import com.android.systemui.settings.brightness.domain.interactor.BrightnessMirrorShowingInteractor
 import com.android.systemui.shade.NotificationShadeWindowView.InteractionEventHandler
 import com.android.systemui.shade.domain.interactor.PanelExpansionInteractor
 import com.android.systemui.statusbar.DragDownHelper
@@ -106,7 +108,7 @@
     @Mock private lateinit var quickSettingsController: QuickSettingsController
     @Mock
     private lateinit var notificationStackScrollLayoutController:
-            NotificationStackScrollLayoutController
+        NotificationStackScrollLayoutController
     @Mock private lateinit var statusBarWindowStateController: StatusBarWindowStateController
     @Mock
     private lateinit var lockscreenShadeTransitionController: LockscreenShadeTransitionController
@@ -122,7 +124,7 @@
     private lateinit var keyguardSecurityContainerController: KeyguardSecurityContainerController
     @Mock
     private lateinit var unfoldTransitionProgressProvider:
-            Optional<UnfoldTransitionProgressProvider>
+        Optional<UnfoldTransitionProgressProvider>
     @Mock private lateinit var notificationInsetsController: NotificationInsetsController
     @Mock private lateinit var mGlanceableHubContainerController: GlanceableHubContainerController
     @Mock private lateinit var keyguardTransitionInteractor: KeyguardTransitionInteractor
@@ -132,6 +134,10 @@
     @Captor
     private lateinit var interactionEventHandlerCaptor: ArgumentCaptor<InteractionEventHandler>
 
+    private val brightnessMirrorShowingRepository = BrightnessMirrorShowingRepository()
+    private val brightnessMirrorShowingInteractor =
+        BrightnessMirrorShowingInteractor(brightnessMirrorShowingRepository)
+
     private lateinit var underTest: NotificationShadeWindowView
     private lateinit var controller: NotificationShadeWindowViewController
     private lateinit var interactionEventHandler: InteractionEventHandler
@@ -142,10 +148,10 @@
         MockitoAnnotations.initMocks(this)
         underTest = spy(NotificationShadeWindowView(context, null))
         whenever(
-            underTest.findViewById<NotificationStackScrollLayout>(
-                R.id.notification_stack_scroller
+                underTest.findViewById<NotificationStackScrollLayout>(
+                    R.id.notification_stack_scroller
+                )
             )
-        )
             .thenReturn(notificationStackScrollLayout)
         whenever(underTest.findViewById<FrameLayout>(R.id.keyguard_bouncer_container))
             .thenReturn(mock())
@@ -198,6 +204,7 @@
                 alternateBouncerInteractor,
                 mock(),
                 configurationForwarder,
+                brightnessMirrorShowingInteractor,
             )
 
         controller.setupExpandedStatusBar()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/carrier/ShadeCarrierGroupControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/carrier/ShadeCarrierGroupControllerTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/shade/carrier/ShadeCarrierGroupControllerTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/shade/carrier/ShadeCarrierGroupControllerTest.java
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/data/repository/ShadeRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/data/repository/ShadeRepositoryImplTest.kt
index 7d4918a..b5043ce 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/data/repository/ShadeRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/data/repository/ShadeRepositoryImplTest.kt
@@ -39,7 +39,7 @@
 
     @Before
     fun setUp() {
-        underTest = ShadeRepositoryImpl(getContext())
+        underTest = ShadeRepositoryImpl()
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerWithCoroutinesTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/KeyguardIndicationControllerWithCoroutinesTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerWithCoroutinesTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/KeyguardIndicationControllerWithCoroutinesTest.kt
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/call/domain/interactor/CallChipInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/call/domain/interactor/CallChipInteractorTest.kt
index 8f41caf..e38ea30 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/call/domain/interactor/CallChipInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/call/domain/interactor/CallChipInteractorTest.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.statusbar.chips.call.domain.interactor
 
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
@@ -27,8 +28,10 @@
 import com.google.common.truth.Truth.assertThat
 import kotlin.test.Test
 import kotlinx.coroutines.test.runTest
+import org.junit.runner.RunWith
 
 @SmallTest
+@RunWith(AndroidJUnit4::class)
 class CallChipInteractorTest : SysuiTestCase() {
     val kosmos = Kosmos()
     val repo = kosmos.ongoingCallRepository
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModelTest.kt
index 7bc6d4a..3ebf9f7 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModelTest.kt
@@ -20,6 +20,7 @@
 import android.platform.test.annotations.DisableFlags
 import android.platform.test.annotations.EnableFlags
 import android.view.View
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.Flags.FLAG_STATUS_BAR_CALL_CHIP_NOTIFICATION_ICON
 import com.android.systemui.SysuiTestCase
@@ -40,11 +41,13 @@
 import com.google.common.truth.Truth.assertThat
 import kotlin.test.Test
 import kotlinx.coroutines.test.runTest
+import org.junit.runner.RunWith
 import org.mockito.kotlin.mock
 import org.mockito.kotlin.verify
 import org.mockito.kotlin.whenever
 
 @SmallTest
+@RunWith(AndroidJUnit4::class)
 class CallChipViewModelTest : SysuiTestCase() {
     private val kosmos = Kosmos()
     private val testScope = kosmos.testScope
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/casttootherdevice/domian/interactor/MediaRouterChipInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/casttootherdevice/domian/interactor/MediaRouterChipInteractorTest.kt
index ecb1a6d..dd0ac1c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/casttootherdevice/domian/interactor/MediaRouterChipInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/casttootherdevice/domian/interactor/MediaRouterChipInteractorTest.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.statusbar.chips.casttootherdevice.domian.interactor
 
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
@@ -30,8 +31,10 @@
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
+import org.junit.runner.RunWith
 
 @SmallTest
+@RunWith(AndroidJUnit4::class)
 @OptIn(ExperimentalCoroutinesApi::class)
 class MediaRouterChipInteractorTest : SysuiTestCase() {
     val kosmos = Kosmos()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/view/EndCastScreenToOtherDeviceDialogDelegateTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/view/EndCastScreenToOtherDeviceDialogDelegateTest.kt
index 5005d16..b297bed 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/view/EndCastScreenToOtherDeviceDialogDelegateTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/view/EndCastScreenToOtherDeviceDialogDelegateTest.kt
@@ -16,6 +16,8 @@
 
 package com.android.systemui.statusbar.chips.casttootherdevice.ui.view
 
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import org.junit.runner.RunWith
 import android.content.ComponentName
 import android.content.DialogInterface
 import android.content.Intent
@@ -49,6 +51,7 @@
 import org.mockito.kotlin.whenever
 
 @SmallTest
+@RunWith(AndroidJUnit4::class)
 @OptIn(ExperimentalCoroutinesApi::class)
 class EndCastScreenToOtherDeviceDialogDelegateTest : SysuiTestCase() {
     private val kosmos = Kosmos().also { it.testCase = this }
@@ -92,7 +95,7 @@
         createAndSetDelegate(
             MediaProjectionState.Projecting.EntireScreen(
                 HOST_PACKAGE,
-                hostDeviceName = "My Favorite Device"
+                hostDeviceName = "My Favorite Device",
             )
         )
 
@@ -118,8 +121,8 @@
             MediaProjectionState.Projecting.SingleTask(
                 HOST_PACKAGE,
                 hostDeviceName = null,
-                createTask(taskId = 1, baseIntent = baseIntent)
-            ),
+                createTask(taskId = 1, baseIntent = baseIntent),
+            )
         )
 
         underTest.beforeCreate(sysuiDialog, /* savedInstanceState= */ null)
@@ -141,8 +144,8 @@
             MediaProjectionState.Projecting.SingleTask(
                 HOST_PACKAGE,
                 hostDeviceName = "My Favorite Device",
-                createTask(taskId = 1, baseIntent = baseIntent)
-            ),
+                createTask(taskId = 1, baseIntent = baseIntent),
+            )
         )
 
         underTest.beforeCreate(sysuiDialog, /* savedInstanceState= */ null)
@@ -169,8 +172,8 @@
             MediaProjectionState.Projecting.SingleTask(
                 HOST_PACKAGE,
                 hostDeviceName = null,
-                createTask(taskId = 1, baseIntent = baseIntent)
-            ),
+                createTask(taskId = 1, baseIntent = baseIntent),
+            )
         )
 
         underTest.beforeCreate(sysuiDialog, /* savedInstanceState= */ null)
@@ -198,7 +201,7 @@
                 HOST_PACKAGE,
                 hostDeviceName = "My Favorite Device",
                 createTask(taskId = 1, baseIntent = baseIntent),
-            ),
+            )
         )
 
         underTest.beforeCreate(sysuiDialog, /* savedInstanceState= */ null)
@@ -235,7 +238,7 @@
             verify(sysuiDialog)
                 .setPositiveButton(
                     eq(R.string.cast_to_other_device_stop_dialog_button),
-                    clickListener.capture()
+                    clickListener.capture(),
                 )
 
             // Verify that clicking the button stops the recording
@@ -254,7 +257,8 @@
                 kosmos.applicationContext,
                 stopAction = kosmos.mediaProjectionChipInteractor::stopProjecting,
                 ProjectionChipModel.Projecting(
-                    ProjectionChipModel.Type.CAST_TO_OTHER_DEVICE,
+                    ProjectionChipModel.Receiver.CastToOtherDevice,
+                    ProjectionChipModel.ContentType.Screen,
                     state,
                 ),
             )
@@ -268,7 +272,7 @@
             MediaProjectionState.Projecting.SingleTask(
                 HOST_PACKAGE,
                 hostDeviceName = null,
-                createTask(taskId = 1)
+                createTask(taskId = 1),
             )
     }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/view/EndGenericCastToOtherDeviceDialogDelegateTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/view/EndGenericCastToOtherDeviceDialogDelegateTest.kt
index e6101f5..9e8f22e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/view/EndGenericCastToOtherDeviceDialogDelegateTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/view/EndGenericCastToOtherDeviceDialogDelegateTest.kt
@@ -18,6 +18,7 @@
 
 import android.content.DialogInterface
 import android.content.applicationContext
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
@@ -35,12 +36,14 @@
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
+import org.junit.runner.RunWith
 import org.mockito.kotlin.argumentCaptor
 import org.mockito.kotlin.eq
 import org.mockito.kotlin.mock
 import org.mockito.kotlin.verify
 
 @SmallTest
+@RunWith(AndroidJUnit4::class)
 @OptIn(ExperimentalCoroutinesApi::class)
 class EndGenericCastToOtherDeviceDialogDelegateTest : SysuiTestCase() {
     private val kosmos = Kosmos().also { it.testCase = this }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/viewmodel/CastToOtherDeviceChipViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/viewmodel/CastToOtherDeviceChipViewModelTest.kt
index 77992db..c511c43 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/viewmodel/CastToOtherDeviceChipViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/viewmodel/CastToOtherDeviceChipViewModelTest.kt
@@ -17,9 +17,12 @@
 package com.android.systemui.statusbar.chips.casttootherdevice.ui.viewmodel
 
 import android.content.DialogInterface
+import android.platform.test.annotations.EnableFlags
 import android.view.View
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.internal.jank.Cuj
+import com.android.systemui.Flags.FLAG_STATUS_BAR_SHOW_AUDIO_ONLY_PROJECTION_CHIP
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.animation.DialogCuj
 import com.android.systemui.animation.mockDialogTransitionAnimator
@@ -51,6 +54,7 @@
 import kotlin.test.Test
 import kotlinx.coroutines.test.runTest
 import org.junit.Before
+import org.junit.runner.RunWith
 import org.mockito.ArgumentMatchers.anyBoolean
 import org.mockito.kotlin.any
 import org.mockito.kotlin.argumentCaptor
@@ -60,6 +64,7 @@
 import org.mockito.kotlin.whenever
 
 @SmallTest
+@RunWith(AndroidJUnit4::class)
 class CastToOtherDeviceChipViewModelTest : SysuiTestCase() {
     private val kosmos = Kosmos().also { it.testCase = this }
     private val testScope = kosmos.testScope
@@ -135,6 +140,29 @@
         }
 
     @Test
+    @EnableFlags(FLAG_STATUS_BAR_SHOW_AUDIO_ONLY_PROJECTION_CHIP)
+    fun chip_projectionIsAudioOnly_otherDevicePackage_isShownAsIconOnly() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.chip)
+            mediaRouterRepo.castDevices.value = emptyList()
+
+            mediaProjectionRepo.mediaProjectionState.value =
+                MediaProjectionState.Projecting.NoScreen(
+                    hostPackage = CAST_TO_OTHER_DEVICES_PACKAGE
+                )
+
+            assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown.IconOnly::class.java)
+            val icon =
+                (((latest as OngoingActivityChipModel.Shown).icon)
+                        as OngoingActivityChipModel.ChipIcon.SingleColorIcon)
+                    .impl as Icon.Resource
+            assertThat(icon.res).isEqualTo(R.drawable.ic_cast_connected)
+            // This content description is just generic "Casting", not "Casting screen"
+            assertThat((icon.contentDescription as ContentDescription.Resource).res)
+                .isEqualTo(R.string.accessibility_casting)
+        }
+
+    @Test
     fun chip_projectionIsEntireScreenState_otherDevicesPackage_isShownAsTimer_forScreen() =
         testScope.runTest {
             val latest by collectLastValue(underTest.chip)
@@ -292,6 +320,18 @@
         }
 
     @Test
+    @EnableFlags(FLAG_STATUS_BAR_SHOW_AUDIO_ONLY_PROJECTION_CHIP)
+    fun chip_projectionIsNoScreenState_normalPackage_isHidden() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.chip)
+
+            mediaProjectionRepo.mediaProjectionState.value =
+                MediaProjectionState.Projecting.NoScreen(NORMAL_PACKAGE)
+
+            assertThat(latest).isInstanceOf(OngoingActivityChipModel.Hidden::class.java)
+        }
+
+    @Test
     fun chip_projectionIsSingleTaskState_normalPackage_isHidden() =
         testScope.runTest {
             val latest by collectLastValue(underTest.chip)
@@ -387,12 +427,7 @@
 
             clickListener!!.onClick(chipView)
             verify(kosmos.mockDialogTransitionAnimator)
-                .showFromView(
-                    eq(mockScreenCastDialog),
-                    eq(chipBackgroundView),
-                    any(),
-                    anyBoolean(),
-                )
+                .showFromView(eq(mockScreenCastDialog), eq(chipBackgroundView), any(), anyBoolean())
         }
 
     @Test
@@ -412,12 +447,7 @@
 
             clickListener!!.onClick(chipView)
             verify(kosmos.mockDialogTransitionAnimator)
-                .showFromView(
-                    eq(mockScreenCastDialog),
-                    eq(chipBackgroundView),
-                    any(),
-                    anyBoolean(),
-                )
+                .showFromView(eq(mockScreenCastDialog), eq(chipBackgroundView), any(), anyBoolean())
         }
 
     @Test
@@ -461,12 +491,7 @@
 
             val cujCaptor = argumentCaptor<DialogCuj>()
             verify(kosmos.mockDialogTransitionAnimator)
-                .showFromView(
-                    any(),
-                    any(),
-                    cujCaptor.capture(),
-                    anyBoolean(),
-                )
+                .showFromView(any(), any(), cujCaptor.capture(), anyBoolean())
 
             assertThat(cujCaptor.firstValue.cujType)
                 .isEqualTo(Cuj.CUJ_STATUS_BAR_LAUNCH_DIALOG_FROM_CHIP)
@@ -494,12 +519,7 @@
 
             val cujCaptor = argumentCaptor<DialogCuj>()
             verify(kosmos.mockDialogTransitionAnimator)
-                .showFromView(
-                    any(),
-                    any(),
-                    cujCaptor.capture(),
-                    anyBoolean(),
-                )
+                .showFromView(any(), any(), cujCaptor.capture(), anyBoolean())
 
             assertThat(cujCaptor.firstValue.cujType)
                 .isEqualTo(Cuj.CUJ_STATUS_BAR_LAUNCH_DIALOG_FROM_CHIP)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractorTest.kt
index d0c5e7a..611318a 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractorTest.kt
@@ -21,7 +21,9 @@
 import android.content.packageManager
 import android.content.pm.PackageManager
 import android.content.pm.ResolveInfo
+import android.platform.test.annotations.EnableFlags
 import androidx.test.filters.SmallTest
+import com.android.systemui.Flags.FLAG_STATUS_BAR_SHOW_AUDIO_ONLY_PROJECTION_CHIP
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.kosmos.Kosmos
@@ -65,7 +67,23 @@
         }
 
     @Test
-    fun projection_singleTaskState_otherDevicesPackage_isCastToOtherDeviceType() =
+    @EnableFlags(FLAG_STATUS_BAR_SHOW_AUDIO_ONLY_PROJECTION_CHIP)
+    fun projection_noScreenState_otherDevicesPackage_isCastToOtherAndAudio() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.projection)
+
+            mediaProjectionRepo.mediaProjectionState.value =
+                MediaProjectionState.Projecting.NoScreen(CAST_TO_OTHER_DEVICES_PACKAGE)
+
+            assertThat(latest).isInstanceOf(ProjectionChipModel.Projecting::class.java)
+            assertThat((latest as ProjectionChipModel.Projecting).receiver)
+                .isEqualTo(ProjectionChipModel.Receiver.CastToOtherDevice)
+            assertThat((latest as ProjectionChipModel.Projecting).contentType)
+                .isEqualTo(ProjectionChipModel.ContentType.Audio)
+        }
+
+    @Test
+    fun projection_singleTaskState_otherDevicesPackage_isCastToOtherAndScreen() =
         testScope.runTest {
             val latest by collectLastValue(underTest.projection)
 
@@ -73,31 +91,49 @@
                 MediaProjectionState.Projecting.SingleTask(
                     CAST_TO_OTHER_DEVICES_PACKAGE,
                     hostDeviceName = null,
-                    createTask(taskId = 1)
+                    createTask(taskId = 1),
                 )
 
             assertThat(latest).isInstanceOf(ProjectionChipModel.Projecting::class.java)
-            assertThat((latest as ProjectionChipModel.Projecting).type)
-                .isEqualTo(ProjectionChipModel.Type.CAST_TO_OTHER_DEVICE)
+            assertThat((latest as ProjectionChipModel.Projecting).receiver)
+                .isEqualTo(ProjectionChipModel.Receiver.CastToOtherDevice)
+            assertThat((latest as ProjectionChipModel.Projecting).contentType)
+                .isEqualTo(ProjectionChipModel.ContentType.Screen)
         }
 
     @Test
-    fun projection_entireScreenState_otherDevicesPackage_isCastToOtherDeviceChipType() =
+    fun projection_entireScreenState_otherDevicesPackage_isCastToOtherAndScreen() =
         testScope.runTest {
             val latest by collectLastValue(underTest.projection)
 
             mediaProjectionRepo.mediaProjectionState.value =
-                MediaProjectionState.Projecting.EntireScreen(
-                    CAST_TO_OTHER_DEVICES_PACKAGE,
-                )
+                MediaProjectionState.Projecting.EntireScreen(CAST_TO_OTHER_DEVICES_PACKAGE)
 
             assertThat(latest).isInstanceOf(ProjectionChipModel.Projecting::class.java)
-            assertThat((latest as ProjectionChipModel.Projecting).type)
-                .isEqualTo(ProjectionChipModel.Type.CAST_TO_OTHER_DEVICE)
+            assertThat((latest as ProjectionChipModel.Projecting).receiver)
+                .isEqualTo(ProjectionChipModel.Receiver.CastToOtherDevice)
+            assertThat((latest as ProjectionChipModel.Projecting).contentType)
+                .isEqualTo(ProjectionChipModel.ContentType.Screen)
         }
 
     @Test
-    fun projection_singleTaskState_normalPackage_isShareToAppChipType() =
+    @EnableFlags(FLAG_STATUS_BAR_SHOW_AUDIO_ONLY_PROJECTION_CHIP)
+    fun projection_noScreenState_normalPackage_isShareToAppAndAudio() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.projection)
+
+            mediaProjectionRepo.mediaProjectionState.value =
+                MediaProjectionState.Projecting.NoScreen(NORMAL_PACKAGE)
+
+            assertThat(latest).isInstanceOf(ProjectionChipModel.Projecting::class.java)
+            assertThat((latest as ProjectionChipModel.Projecting).receiver)
+                .isEqualTo(ProjectionChipModel.Receiver.ShareToApp)
+            assertThat((latest as ProjectionChipModel.Projecting).contentType)
+                .isEqualTo(ProjectionChipModel.ContentType.Audio)
+        }
+
+    @Test
+    fun projection_singleTaskState_normalPackage_isShareToAppAndScreen() =
         testScope.runTest {
             val latest by collectLastValue(underTest.projection)
 
@@ -109,12 +145,14 @@
                 )
 
             assertThat(latest).isInstanceOf(ProjectionChipModel.Projecting::class.java)
-            assertThat((latest as ProjectionChipModel.Projecting).type)
-                .isEqualTo(ProjectionChipModel.Type.SHARE_TO_APP)
+            assertThat((latest as ProjectionChipModel.Projecting).receiver)
+                .isEqualTo(ProjectionChipModel.Receiver.ShareToApp)
+            assertThat((latest as ProjectionChipModel.Projecting).contentType)
+                .isEqualTo(ProjectionChipModel.ContentType.Screen)
         }
 
     @Test
-    fun projection_entireScreenState_normalPackage_isShareToAppChipType() =
+    fun projection_entireScreenState_normalPackage_isShareToAppAndScreen() =
         testScope.runTest {
             val latest by collectLastValue(underTest.projection)
 
@@ -122,8 +160,10 @@
                 MediaProjectionState.Projecting.EntireScreen(NORMAL_PACKAGE)
 
             assertThat(latest).isInstanceOf(ProjectionChipModel.Projecting::class.java)
-            assertThat((latest as ProjectionChipModel.Projecting).type)
-                .isEqualTo(ProjectionChipModel.Type.SHARE_TO_APP)
+            assertThat((latest as ProjectionChipModel.Projecting).receiver)
+                .isEqualTo(ProjectionChipModel.Receiver.ShareToApp)
+            assertThat((latest as ProjectionChipModel.Projecting).contentType)
+                .isEqualTo(ProjectionChipModel.ContentType.Screen)
         }
 
     companion object {
@@ -140,14 +180,14 @@
                 whenever(
                         this.checkPermission(
                             Manifest.permission.REMOTE_DISPLAY_PROVIDER,
-                            CAST_TO_OTHER_DEVICES_PACKAGE
+                            CAST_TO_OTHER_DEVICES_PACKAGE,
                         )
                     )
                     .thenReturn(PackageManager.PERMISSION_GRANTED)
                 whenever(
                         this.checkPermission(
                             Manifest.permission.REMOTE_DISPLAY_PROVIDER,
-                            NORMAL_PACKAGE
+                            NORMAL_PACKAGE,
                         )
                     )
                     .thenReturn(PackageManager.PERMISSION_DENIED)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/mediaprojection/ui/view/EndMediaProjectionDialogHelperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/mediaprojection/ui/view/EndMediaProjectionDialogHelperTest.kt
index c62e404..795988f 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/mediaprojection/ui/view/EndMediaProjectionDialogHelperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/mediaprojection/ui/view/EndMediaProjectionDialogHelperTest.kt
@@ -21,6 +21,7 @@
 import android.content.packageManager
 import android.content.pm.ApplicationInfo
 import android.content.pm.PackageManager
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.kosmos.Kosmos
@@ -31,6 +32,7 @@
 import com.android.systemui.statusbar.phone.mockSystemUIDialogFactory
 import com.google.common.truth.Truth.assertThat
 import kotlin.test.Test
+import org.junit.runner.RunWith
 import org.mockito.kotlin.any
 import org.mockito.kotlin.eq
 import org.mockito.kotlin.mock
@@ -38,6 +40,7 @@
 import org.mockito.kotlin.whenever
 
 @SmallTest
+@RunWith(AndroidJUnit4::class)
 class EndMediaProjectionDialogHelperTest : SysuiTestCase() {
     private val kosmos = Kosmos().also { it.testCase = this }
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/domain/interactor/StatusBarNotificationChipsInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/domain/interactor/StatusBarNotificationChipsInteractorTest.kt
new file mode 100644
index 0000000..19ed6a5
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/domain/interactor/StatusBarNotificationChipsInteractorTest.kt
@@ -0,0 +1,70 @@
+/*
+ * 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.chips.notification.domain.interactor
+
+import android.platform.test.annotations.EnableFlags
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectValues
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.kosmos.useUnconfinedTestDispatcher
+import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlin.test.Test
+import kotlinx.coroutines.test.runTest
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+@EnableFlags(StatusBarNotifChips.FLAG_NAME)
+class StatusBarNotificationChipsInteractorTest : SysuiTestCase() {
+    private val kosmos = testKosmos().useUnconfinedTestDispatcher()
+    private val testScope = kosmos.testScope
+
+    private val underTest = kosmos.statusBarNotificationChipsInteractor
+
+    @Test
+    fun onPromotedNotificationChipTapped_emitsKeys() =
+        testScope.runTest {
+            val latest by collectValues(underTest.promotedNotificationChipTapEvent)
+
+            underTest.onPromotedNotificationChipTapped("fakeKey")
+
+            assertThat(latest).hasSize(1)
+            assertThat(latest[0]).isEqualTo("fakeKey")
+
+            underTest.onPromotedNotificationChipTapped("fakeKey2")
+
+            assertThat(latest).hasSize(2)
+            assertThat(latest[1]).isEqualTo("fakeKey2")
+        }
+
+    @Test
+    fun onPromotedNotificationChipTapped_sameKeyTwice_emitsTwice() =
+        testScope.runTest {
+            val latest by collectValues(underTest.promotedNotificationChipTapEvent)
+
+            underTest.onPromotedNotificationChipTapped("fakeKey")
+            underTest.onPromotedNotificationChipTapped("fakeKey")
+
+            assertThat(latest).hasSize(2)
+            assertThat(latest[0]).isEqualTo("fakeKey")
+            assertThat(latest[1]).isEqualTo("fakeKey")
+        }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelTest.kt
index eb5d931..6e19096 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelTest.kt
@@ -17,12 +17,14 @@
 package com.android.systemui.statusbar.chips.notification.ui.viewmodel
 
 import android.platform.test.annotations.EnableFlags
+import android.view.View
 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.testScope
 import com.android.systemui.statusbar.StatusBarIconView
+import com.android.systemui.statusbar.chips.notification.domain.interactor.statusBarNotificationChipsInteractor
 import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips
 import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
 import com.android.systemui.statusbar.notification.data.model.activeNotificationModel
@@ -102,6 +104,30 @@
             assertIsNotifChip(latest!![1], secondIcon)
         }
 
+    @Test
+    fun chips_clickingChipNotifiesInteractor() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.chips)
+            val latestChipTap by
+                collectLastValue(
+                    kosmos.statusBarNotificationChipsInteractor.promotedNotificationChipTapEvent
+                )
+
+            setNotifs(
+                listOf(
+                    activeNotificationModel(
+                        key = "clickTest",
+                        statusBarChipIcon = mock<StatusBarIconView>(),
+                    )
+                )
+            )
+            val chip = latest!![0]
+
+            chip.onClickListener!!.onClick(mock<View>())
+
+            assertThat(latestChipTap).isEqualTo("clickTest")
+        }
+
     private fun setNotifs(notifs: List<ActiveNotificationModel>) {
         activeNotificationListRepository.activeNotifications.value =
             ActiveNotificationsStore.Builder()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/screenrecord/domain/interactor/ScreenRecordChipInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/screenrecord/domain/interactor/ScreenRecordChipInteractorTest.kt
index 6bfb40f..0efd591 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/screenrecord/domain/interactor/ScreenRecordChipInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/screenrecord/domain/interactor/ScreenRecordChipInteractorTest.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.statusbar.chips.screenrecord.domain.interactor
 
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
@@ -32,8 +33,10 @@
 import kotlin.test.Test
 import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
+import org.junit.runner.RunWith
 
 @SmallTest
+@RunWith(AndroidJUnit4::class)
 class ScreenRecordChipInteractorTest : SysuiTestCase() {
     private val kosmos = Kosmos().also { it.testCase = this }
     private val testScope = kosmos.testScope
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/screenrecord/ui/view/EndScreenRecordingDialogDelegateTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/screenrecord/ui/view/EndScreenRecordingDialogDelegateTest.kt
index bfb63ac..709e0b5 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/screenrecord/ui/view/EndScreenRecordingDialogDelegateTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/screenrecord/ui/view/EndScreenRecordingDialogDelegateTest.kt
@@ -23,6 +23,7 @@
 import android.content.applicationContext
 import android.content.packageManager
 import android.content.pm.ApplicationInfo
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.kosmos.Kosmos
@@ -39,6 +40,7 @@
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
+import org.junit.runner.RunWith
 import org.mockito.kotlin.any
 import org.mockito.kotlin.argumentCaptor
 import org.mockito.kotlin.eq
@@ -47,6 +49,7 @@
 import org.mockito.kotlin.whenever
 
 @SmallTest
+@RunWith(AndroidJUnit4::class)
 @OptIn(ExperimentalCoroutinesApi::class)
 class EndScreenRecordingDialogDelegateTest : SysuiTestCase() {
     private val kosmos = Kosmos().also { it.testCase = this }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/screenrecord/ui/viewmodel/ScreenRecordChipViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/screenrecord/ui/viewmodel/ScreenRecordChipViewModelTest.kt
index 16101bf..bfebe18 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/screenrecord/ui/viewmodel/ScreenRecordChipViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/screenrecord/ui/viewmodel/ScreenRecordChipViewModelTest.kt
@@ -18,6 +18,7 @@
 
 import android.content.DialogInterface
 import android.view.View
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.internal.jank.Cuj
 import com.android.systemui.SysuiTestCase
@@ -48,6 +49,7 @@
 import kotlin.test.Test
 import kotlinx.coroutines.test.runTest
 import org.junit.Before
+import org.junit.runner.RunWith
 import org.mockito.ArgumentMatchers.anyBoolean
 import org.mockito.kotlin.any
 import org.mockito.kotlin.argumentCaptor
@@ -57,6 +59,7 @@
 import org.mockito.kotlin.whenever
 
 @SmallTest
+@RunWith(AndroidJUnit4::class)
 class ScreenRecordChipViewModelTest : SysuiTestCase() {
     private val kosmos = Kosmos().also { it.testCase = this }
     private val testScope = kosmos.testScope
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/sharetoapp/ui/view/EndGenericShareToAppDialogDelegateTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/sharetoapp/ui/view/EndGenericShareToAppDialogDelegateTest.kt
new file mode 100644
index 0000000..411d306
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/sharetoapp/ui/view/EndGenericShareToAppDialogDelegateTest.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.statusbar.chips.sharetoapp.ui.view
+
+import android.content.DialogInterface
+import android.content.applicationContext
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.mediaprojection.data.repository.fakeMediaProjectionRepository
+import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.mediaProjectionChipInteractor
+import com.android.systemui.statusbar.chips.mediaprojection.ui.view.endMediaProjectionDialogHelper
+import com.android.systemui.statusbar.phone.SystemUIDialog
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlin.test.Test
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.mockito.kotlin.any
+import org.mockito.kotlin.argumentCaptor
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.verify
+
+@SmallTest
+@OptIn(ExperimentalCoroutinesApi::class)
+class EndGenericShareToAppDialogDelegateTest : SysuiTestCase() {
+    private val kosmos = testKosmos()
+    private val sysuiDialog = mock<SystemUIDialog>()
+    private val underTest =
+        EndGenericShareToAppDialogDelegate(
+            kosmos.endMediaProjectionDialogHelper,
+            kosmos.applicationContext,
+            stopAction = kosmos.mediaProjectionChipInteractor::stopProjecting,
+        )
+
+    @Test
+    fun positiveButton_clickStopsRecording() =
+        kosmos.testScope.runTest {
+            underTest.beforeCreate(sysuiDialog, /* savedInstanceState= */ null)
+
+            assertThat(kosmos.fakeMediaProjectionRepository.stopProjectingInvoked).isFalse()
+
+            val clickListener = argumentCaptor<DialogInterface.OnClickListener>()
+            verify(sysuiDialog).setPositiveButton(any(), clickListener.capture())
+            clickListener.firstValue.onClick(mock<DialogInterface>(), 0)
+            runCurrent()
+
+            assertThat(kosmos.fakeMediaProjectionRepository.stopProjectingInvoked).isTrue()
+        }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/sharetoapp/ui/view/EndShareToAppDialogDelegateTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/sharetoapp/ui/view/EndShareScreenToAppDialogDelegateTest.kt
similarity index 93%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/sharetoapp/ui/view/EndShareToAppDialogDelegateTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/sharetoapp/ui/view/EndShareScreenToAppDialogDelegateTest.kt
index 325a42b..6885a6b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/sharetoapp/ui/view/EndShareToAppDialogDelegateTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/sharetoapp/ui/view/EndShareScreenToAppDialogDelegateTest.kt
@@ -50,10 +50,10 @@
 
 @SmallTest
 @OptIn(ExperimentalCoroutinesApi::class)
-class EndShareToAppDialogDelegateTest : SysuiTestCase() {
+class EndShareScreenToAppDialogDelegateTest : SysuiTestCase() {
     private val kosmos = Kosmos().also { it.testCase = this }
     private val sysuiDialog = mock<SystemUIDialog>()
-    private lateinit var underTest: EndShareToAppDialogDelegate
+    private lateinit var underTest: EndShareScreenToAppDialogDelegate
 
     @Test
     fun icon() {
@@ -117,7 +117,7 @@
             MediaProjectionState.Projecting.SingleTask(
                 HOST_PACKAGE,
                 hostDeviceName = null,
-                createTask(taskId = 1, baseIntent = baseIntent)
+                createTask(taskId = 1, baseIntent = baseIntent),
             )
         )
 
@@ -142,7 +142,7 @@
             MediaProjectionState.Projecting.SingleTask(
                 HOST_PACKAGE,
                 hostDeviceName = null,
-                createTask(taskId = 1, baseIntent = baseIntent)
+                createTask(taskId = 1, baseIntent = baseIntent),
             )
         )
 
@@ -181,7 +181,7 @@
             verify(sysuiDialog)
                 .setPositiveButton(
                     eq(R.string.share_to_app_stop_dialog_button),
-                    clickListener.capture()
+                    clickListener.capture(),
                 )
 
             // Verify that clicking the button stops the recording
@@ -195,12 +195,13 @@
 
     private fun createAndSetDelegate(state: MediaProjectionState.Projecting) {
         underTest =
-            EndShareToAppDialogDelegate(
+            EndShareScreenToAppDialogDelegate(
                 kosmos.endMediaProjectionDialogHelper,
                 kosmos.applicationContext,
                 stopAction = kosmos.mediaProjectionChipInteractor::stopProjecting,
                 ProjectionChipModel.Projecting(
-                    ProjectionChipModel.Type.SHARE_TO_APP,
+                    ProjectionChipModel.Receiver.ShareToApp,
+                    ProjectionChipModel.ContentType.Screen,
                     state,
                 ),
             )
@@ -213,7 +214,7 @@
             MediaProjectionState.Projecting.SingleTask(
                 HOST_PACKAGE,
                 hostDeviceName = null,
-                createTask(taskId = 1)
+                createTask(taskId = 1),
             )
     }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/sharetoapp/ui/viewmodel/ShareToAppChipViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/sharetoapp/ui/viewmodel/ShareToAppChipViewModelTest.kt
index 791a21d..b3dec2e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/sharetoapp/ui/viewmodel/ShareToAppChipViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/sharetoapp/ui/viewmodel/ShareToAppChipViewModelTest.kt
@@ -17,12 +17,16 @@
 package com.android.systemui.statusbar.chips.sharetoapp.ui.viewmodel
 
 import android.content.DialogInterface
+import android.platform.test.annotations.EnableFlags
 import android.view.View
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.internal.jank.Cuj
+import com.android.systemui.Flags.FLAG_STATUS_BAR_SHOW_AUDIO_ONLY_PROJECTION_CHIP
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.animation.DialogCuj
 import com.android.systemui.animation.mockDialogTransitionAnimator
+import com.android.systemui.common.shared.model.ContentDescription
 import com.android.systemui.common.shared.model.Icon
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.kosmos.Kosmos
@@ -35,7 +39,8 @@
 import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.MediaProjectionChipInteractorTest.Companion.CAST_TO_OTHER_DEVICES_PACKAGE
 import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.MediaProjectionChipInteractorTest.Companion.NORMAL_PACKAGE
 import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.MediaProjectionChipInteractorTest.Companion.setUpPackageManagerForMediaProjection
-import com.android.systemui.statusbar.chips.sharetoapp.ui.view.EndShareToAppDialogDelegate
+import com.android.systemui.statusbar.chips.sharetoapp.ui.view.EndGenericShareToAppDialogDelegate
+import com.android.systemui.statusbar.chips.sharetoapp.ui.view.EndShareScreenToAppDialogDelegate
 import com.android.systemui.statusbar.chips.ui.model.ColorsModel
 import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
 import com.android.systemui.statusbar.chips.ui.view.ChipBackgroundContainer
@@ -47,6 +52,7 @@
 import kotlin.test.Test
 import kotlinx.coroutines.test.runTest
 import org.junit.Before
+import org.junit.runner.RunWith
 import org.mockito.ArgumentMatchers.anyBoolean
 import org.mockito.kotlin.any
 import org.mockito.kotlin.argumentCaptor
@@ -56,13 +62,15 @@
 import org.mockito.kotlin.whenever
 
 @SmallTest
+@RunWith(AndroidJUnit4::class)
 class ShareToAppChipViewModelTest : SysuiTestCase() {
     private val kosmos = Kosmos().also { it.testCase = this }
     private val testScope = kosmos.testScope
     private val mediaProjectionRepo = kosmos.fakeMediaProjectionRepository
     private val systemClock = kosmos.fakeSystemClock
 
-    private val mockShareDialog = mock<SystemUIDialog>()
+    private val mockScreenShareDialog = mock<SystemUIDialog>()
+    private val mockGenericShareDialog = mock<SystemUIDialog>()
     private val chipBackgroundView = mock<ChipBackgroundContainer>()
     private val chipView =
         mock<View>().apply {
@@ -80,8 +88,10 @@
     fun setUp() {
         setUpPackageManagerForMediaProjection(kosmos)
 
-        whenever(kosmos.mockSystemUIDialogFactory.create(any<EndShareToAppDialogDelegate>()))
-            .thenReturn(mockShareDialog)
+        whenever(kosmos.mockSystemUIDialogFactory.create(any<EndShareScreenToAppDialogDelegate>()))
+            .thenReturn(mockScreenShareDialog)
+        whenever(kosmos.mockSystemUIDialogFactory.create(any<EndGenericShareToAppDialogDelegate>()))
+            .thenReturn(mockGenericShareDialog)
     }
 
     @Test
@@ -95,6 +105,21 @@
         }
 
     @Test
+    @EnableFlags(FLAG_STATUS_BAR_SHOW_AUDIO_ONLY_PROJECTION_CHIP)
+    fun chip_noScreenState_otherDevicesPackage_isHidden() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.chip)
+
+            mediaProjectionRepo.mediaProjectionState.value =
+                MediaProjectionState.Projecting.NoScreen(
+                    CAST_TO_OTHER_DEVICES_PACKAGE,
+                    hostDeviceName = null,
+                )
+
+            assertThat(latest).isInstanceOf(OngoingActivityChipModel.Hidden::class.java)
+        }
+
+    @Test
     fun chip_singleTaskState_otherDevicesPackage_isHidden() =
         testScope.runTest {
             val latest by collectLastValue(underTest.chip)
@@ -121,6 +146,26 @@
         }
 
     @Test
+    @EnableFlags(FLAG_STATUS_BAR_SHOW_AUDIO_ONLY_PROJECTION_CHIP)
+    fun chip_noScreenState_normalPackage_isShownAsIconOnly() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.chip)
+
+            mediaProjectionRepo.mediaProjectionState.value =
+                MediaProjectionState.Projecting.NoScreen(NORMAL_PACKAGE, hostDeviceName = null)
+
+            assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown.IconOnly::class.java)
+            val icon =
+                (((latest as OngoingActivityChipModel.Shown).icon)
+                        as OngoingActivityChipModel.ChipIcon.SingleColorIcon)
+                    .impl as Icon.Resource
+            assertThat(icon.res).isEqualTo(R.drawable.ic_present_to_all)
+            // This content description is just generic "Sharing content", not "Sharing screen"
+            assertThat((icon.contentDescription as ContentDescription.Resource).res)
+                .isEqualTo(R.string.share_to_app_chip_accessibility_label_generic)
+        }
+
+    @Test
     fun chip_singleTaskState_normalPackage_isShownAsTimer() =
         testScope.runTest {
             val latest by collectLastValue(underTest.chip)
@@ -170,7 +215,7 @@
 
             // WHEN the stop action on the dialog is clicked
             val dialogStopAction =
-                getStopActionFromDialog(latest, chipView, mockShareDialog, kosmos)
+                getStopActionFromDialog(latest, chipView, mockScreenShareDialog, kosmos)
             dialogStopAction.onClick(mock<DialogInterface>(), 0)
 
             // THEN the chip is immediately hidden...
@@ -222,7 +267,28 @@
         }
 
     @Test
-    fun chip_entireScreen_clickListenerShowsShareDialog() =
+    @EnableFlags(FLAG_STATUS_BAR_SHOW_AUDIO_ONLY_PROJECTION_CHIP)
+    fun chip_noScreen_clickListenerShowsGenericShareDialog() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.chip)
+            mediaProjectionRepo.mediaProjectionState.value =
+                MediaProjectionState.Projecting.NoScreen(NORMAL_PACKAGE)
+
+            val clickListener = ((latest as OngoingActivityChipModel.Shown).onClickListener)
+            assertThat(clickListener).isNotNull()
+
+            clickListener!!.onClick(chipView)
+            verify(kosmos.mockDialogTransitionAnimator)
+                .showFromView(
+                    eq(mockGenericShareDialog),
+                    eq(chipBackgroundView),
+                    any(),
+                    anyBoolean(),
+                )
+        }
+
+    @Test
+    fun chip_entireScreen_clickListenerShowsScreenShareDialog() =
         testScope.runTest {
             val latest by collectLastValue(underTest.chip)
             mediaProjectionRepo.mediaProjectionState.value =
@@ -234,7 +300,7 @@
             clickListener!!.onClick(chipView)
             verify(kosmos.mockDialogTransitionAnimator)
                 .showFromView(
-                    eq(mockShareDialog),
+                    eq(mockScreenShareDialog),
                     eq(chipBackgroundView),
                     any(),
                     anyBoolean(),
@@ -242,7 +308,7 @@
         }
 
     @Test
-    fun chip_singleTask_clickListenerShowsShareDialog() =
+    fun chip_singleTask_clickListenerShowsScreenShareDialog() =
         testScope.runTest {
             val latest by collectLastValue(underTest.chip)
             mediaProjectionRepo.mediaProjectionState.value =
@@ -258,7 +324,7 @@
             clickListener!!.onClick(chipView)
             verify(kosmos.mockDialogTransitionAnimator)
                 .showFromView(
-                    eq(mockShareDialog),
+                    eq(mockScreenShareDialog),
                     eq(chipBackgroundView),
                     any(),
                     anyBoolean(),
@@ -281,12 +347,7 @@
 
             val cujCaptor = argumentCaptor<DialogCuj>()
             verify(kosmos.mockDialogTransitionAnimator)
-                .showFromView(
-                    any(),
-                    any(),
-                    cujCaptor.capture(),
-                    anyBoolean(),
-                )
+                .showFromView(any(), any(), cujCaptor.capture(), anyBoolean())
 
             assertThat(cujCaptor.firstValue.cujType)
                 .isEqualTo(Cuj.CUJ_STATUS_BAR_LAUNCH_DIALOG_FROM_CHIP)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/ChipTransitionHelperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/ChipTransitionHelperTest.kt
index 4977c548..8d4c68d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/ChipTransitionHelperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/ChipTransitionHelperTest.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.statusbar.chips.ui.viewmodel
 
 import androidx.annotation.DrawableRes
+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
@@ -34,8 +35,10 @@
 import kotlinx.coroutines.test.advanceTimeBy
 import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
+import org.junit.runner.RunWith
 
 @SmallTest
+@RunWith(AndroidJUnit4::class)
 @OptIn(ExperimentalCoroutinesApi::class)
 class ChipTransitionHelperTest : SysuiTestCase() {
     private val kosmos = Kosmos()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipViewModelTest.kt
index 6e4d886..e3510f5 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipViewModelTest.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.statusbar.chips.ui.viewmodel
 
 import android.view.View
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.internal.jank.Cuj
 import com.android.systemui.SysuiTestCase
@@ -28,6 +29,7 @@
 import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipViewModel.Companion.createDialogLaunchOnClickListener
 import com.android.systemui.statusbar.phone.SystemUIDialog
 import kotlin.test.Test
+import org.junit.runner.RunWith
 import org.mockito.ArgumentMatchers.anyBoolean
 import org.mockito.kotlin.eq
 import org.mockito.kotlin.mock
@@ -35,6 +37,7 @@
 import org.mockito.kotlin.whenever
 
 @SmallTest
+@RunWith(AndroidJUnit4::class)
 class OngoingActivityChipViewModelTest : SysuiTestCase() {
     private val mockSystemUIDialog = mock<SystemUIDialog>()
     private val dialogDelegate = SystemUIDialog.Delegate { mockSystemUIDialog }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/commandline/CommandRegistryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/commandline/CommandRegistryTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/commandline/CommandRegistryTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/commandline/CommandRegistryTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerBaseTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/connectivity/NetworkControllerBaseTest.java
similarity index 99%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerBaseTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/connectivity/NetworkControllerBaseTest.java
index b00f9e9..f502cab 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerBaseTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/connectivity/NetworkControllerBaseTest.java
@@ -61,6 +61,7 @@
 import android.util.Log;
 
 import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
 
 import com.android.settingslib.R;
 import com.android.settingslib.graph.SignalDrawable;
@@ -98,6 +99,7 @@
 import java.util.Collections;
 import java.util.List;
 
+@SmallTest
 public class NetworkControllerBaseTest extends SysuiTestCase {
     private static final String TAG = "NetworkControllerBaseTest";
     protected static final int DEFAULT_LEVEL = 2;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerDataTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/connectivity/NetworkControllerDataTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerDataTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/connectivity/NetworkControllerDataTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerEthernetTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/connectivity/NetworkControllerEthernetTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerEthernetTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/connectivity/NetworkControllerEthernetTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerWifiTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/connectivity/NetworkControllerWifiTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerWifiTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/connectivity/NetworkControllerWifiTest.java
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/StatusBarOrchestratorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/StatusBarOrchestratorTest.kt
index 7923097..659e53f 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/StatusBarOrchestratorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/StatusBarOrchestratorTest.kt
@@ -22,9 +22,8 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.bouncer.data.repository.fakeKeyguardBouncerRepository
-import com.android.systemui.kosmos.testDispatcher
 import com.android.systemui.kosmos.testScope
-import com.android.systemui.kosmos.unconfinedTestDispatcher
+import com.android.systemui.kosmos.useUnconfinedTestDispatcher
 import com.android.systemui.plugins.DarkIconDispatcher
 import com.android.systemui.plugins.mockPluginDependencyProvider
 import com.android.systemui.plugins.statusbar.StatusBarStateController
@@ -57,7 +56,7 @@
 @RunWith(AndroidJUnit4::class)
 class StatusBarOrchestratorTest : SysuiTestCase() {
 
-    private val kosmos = testKosmos().also { it.testDispatcher = it.unconfinedTestDispatcher }
+    private val kosmos = testKosmos().useUnconfinedTestDispatcher()
     private val testScope = kosmos.testScope
     private val fakeStatusBarModePerDisplayRepository = kosmos.fakeStatusBarModePerDisplayRepository
     private val mockPluginDependencyProvider = kosmos.mockPluginDependencyProvider
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/MultiDisplayStatusBarContentInsetsProviderStoreTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/MultiDisplayStatusBarContentInsetsProviderStoreTest.kt
index 0eebab0..4a26fdf 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/MultiDisplayStatusBarContentInsetsProviderStoreTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/MultiDisplayStatusBarContentInsetsProviderStoreTest.kt
@@ -21,9 +21,8 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.display.data.repository.displayRepository
-import com.android.systemui.kosmos.testDispatcher
 import com.android.systemui.kosmos.testScope
-import com.android.systemui.kosmos.unconfinedTestDispatcher
+import com.android.systemui.kosmos.useUnconfinedTestDispatcher
 import com.android.systemui.testKosmos
 import kotlinx.coroutines.runBlocking
 import kotlinx.coroutines.test.runTest
@@ -37,7 +36,7 @@
 @RunWith(AndroidJUnit4::class)
 class MultiDisplayStatusBarContentInsetsProviderStoreTest : SysuiTestCase() {
 
-    private val kosmos = testKosmos().also { it.testDispatcher = it.unconfinedTestDispatcher }
+    private val kosmos = testKosmos().useUnconfinedTestDispatcher()
     private val testScope = kosmos.testScope
     private val fakeDisplayRepository = kosmos.displayRepository
     private val underTest = kosmos.multiDisplayStatusBarContentInsetsProviderStore
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/events/PrivacyDotViewControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/events/PrivacyDotViewControllerTest.kt
index 16da3d2..4795a12 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/events/PrivacyDotViewControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/events/PrivacyDotViewControllerTest.kt
@@ -29,6 +29,10 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.res.R
 import com.android.systemui.statusbar.FakeStatusBarStateController
+import com.android.systemui.statusbar.events.PrivacyDotCorner.BottomLeft
+import com.android.systemui.statusbar.events.PrivacyDotCorner.BottomRight
+import com.android.systemui.statusbar.events.PrivacyDotCorner.TopLeft
+import com.android.systemui.statusbar.events.PrivacyDotCorner.TopRight
 import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider
 import com.android.systemui.statusbar.policy.FakeConfigurationController
 import com.android.systemui.util.concurrency.DelayableExecutor
@@ -71,15 +75,15 @@
 
     private fun createController() =
         PrivacyDotViewControllerImpl(
-                executor,
-                testScope.backgroundScope,
-                statusBarStateController,
-                configurationController,
-                contentInsetsProvider,
-                animationScheduler = mock<SystemStatusAnimationScheduler>(),
-                shadeInteractor = null,
-            )
-            .also { it.setUiExecutor(executor) }
+            executor,
+            testScope.backgroundScope,
+            statusBarStateController,
+            configurationController,
+            contentInsetsProvider,
+            animationScheduler = mock<SystemStatusAnimationScheduler>(),
+            shadeInteractor = null,
+            uiExecutor = executor,
+        )
 
     @Test
     fun topMargin_topLeftView_basedOnSeascapeArea() {
@@ -215,7 +219,7 @@
 
         val controller = createAndInitializeController()
 
-        assertThat(controller.currentViewState.cornerIndex).isEqualTo(TOP_RIGHT)
+        assertThat(controller.currentViewState.corner).isEqualTo(TopRight)
         assertThat(controller.currentViewState.designatedCorner).isEqualTo(topRightView)
     }
 
@@ -225,7 +229,7 @@
 
         val controller = createAndInitializeController()
 
-        assertThat(controller.currentViewState.cornerIndex).isEqualTo(BOTTOM_RIGHT)
+        assertThat(controller.currentViewState.corner).isEqualTo(BottomRight)
         assertThat(controller.currentViewState.designatedCorner).isEqualTo(bottomRightView)
     }
 
@@ -235,7 +239,7 @@
 
         val controller = createAndInitializeController()
 
-        assertThat(controller.currentViewState.cornerIndex).isEqualTo(TOP_LEFT)
+        assertThat(controller.currentViewState.corner).isEqualTo(TopLeft)
         assertThat(controller.currentViewState.designatedCorner).isEqualTo(topLeftView)
     }
 
@@ -245,7 +249,7 @@
 
         val controller = createAndInitializeController()
 
-        assertThat(controller.currentViewState.cornerIndex).isEqualTo(BOTTOM_LEFT)
+        assertThat(controller.currentViewState.corner).isEqualTo(BottomLeft)
         assertThat(controller.currentViewState.designatedCorner).isEqualTo(bottomLeftView)
     }
 
@@ -256,7 +260,7 @@
         enableRtl()
         val controller = createAndInitializeController()
 
-        assertThat(controller.currentViewState.cornerIndex).isEqualTo(TOP_LEFT)
+        assertThat(controller.currentViewState.corner).isEqualTo(TopLeft)
         assertThat(controller.currentViewState.designatedCorner).isEqualTo(topLeftView)
     }
 
@@ -267,7 +271,7 @@
         enableRtl()
         val controller = createAndInitializeController()
 
-        assertThat(controller.currentViewState.cornerIndex).isEqualTo(TOP_RIGHT)
+        assertThat(controller.currentViewState.corner).isEqualTo(TopRight)
         assertThat(controller.currentViewState.designatedCorner).isEqualTo(topRightView)
     }
 
@@ -278,7 +282,7 @@
         enableRtl()
         val controller = createAndInitializeController()
 
-        assertThat(controller.currentViewState.cornerIndex).isEqualTo(BOTTOM_LEFT)
+        assertThat(controller.currentViewState.corner).isEqualTo(BottomLeft)
         assertThat(controller.currentViewState.designatedCorner).isEqualTo(bottomLeftView)
     }
 
@@ -289,7 +293,7 @@
         enableRtl()
         val controller = createAndInitializeController()
 
-        assertThat(controller.currentViewState.cornerIndex).isEqualTo(BOTTOM_RIGHT)
+        assertThat(controller.currentViewState.corner).isEqualTo(BottomRight)
         assertThat(controller.currentViewState.designatedCorner).isEqualTo(bottomRightView)
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/AssistantFeedbackControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/AssistantFeedbackControllerTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/AssistantFeedbackControllerTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/AssistantFeedbackControllerTest.java
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt
index 76bb8de..9613f76 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt
@@ -17,12 +17,18 @@
 
 import android.app.Notification.GROUP_ALERT_ALL
 import android.app.Notification.GROUP_ALERT_SUMMARY
+import android.platform.test.annotations.EnableFlags
 import android.testing.TestableLooper.RunWithLooper
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.kosmos.testScope
 import com.android.systemui.log.logcatLogBuffer
 import com.android.systemui.statusbar.NotificationRemoteInputManager
+import com.android.systemui.statusbar.chips.notification.domain.interactor.statusBarNotificationChipsInteractor
+import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips
+import com.android.systemui.statusbar.notification.HeadsUpManagerPhone
 import com.android.systemui.statusbar.notification.NotifPipelineFlags
 import com.android.systemui.statusbar.notification.collection.GroupEntryBuilder
 import com.android.systemui.statusbar.notification.collection.NotifPipeline
@@ -32,6 +38,8 @@
 import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeTransformGroupsListener
 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.mockNotifCollection
+import com.android.systemui.statusbar.notification.collection.notifCollection
 import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener
 import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender
 import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender.OnEndLifetimeExtensionCallback
@@ -43,9 +51,9 @@
 import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderWrapper.FullScreenIntentDecisionImpl
 import com.android.systemui.statusbar.notification.interruption.VisualInterruptionDecisionProvider
 import com.android.systemui.statusbar.notification.row.NotifBindPipeline.BindCallback
-import com.android.systemui.statusbar.notification.HeadsUpManagerPhone
 import com.android.systemui.statusbar.phone.NotificationGroupTestHelper
 import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener
+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.eq
@@ -54,6 +62,7 @@
 import com.android.systemui.util.time.FakeSystemClock
 import java.util.ArrayList
 import java.util.function.Consumer
+import kotlinx.coroutines.test.runTest
 import org.junit.Assert.assertEquals
 import org.junit.Assert.assertFalse
 import org.junit.Assert.assertTrue
@@ -73,6 +82,11 @@
 @RunWith(AndroidJUnit4::class)
 @RunWithLooper
 class HeadsUpCoordinatorTest : SysuiTestCase() {
+    private val kosmos = testKosmos()
+    private val testScope = kosmos.testScope
+    private val statusBarNotificationChipsInteractor = kosmos.statusBarNotificationChipsInteractor
+    private val notifCollection = kosmos.mockNotifCollection
+
     private lateinit var coordinator: HeadsUpCoordinator
 
     // captured listeners and pluggables:
@@ -115,16 +129,19 @@
         helper = NotificationGroupTestHelper(mContext)
         coordinator =
             HeadsUpCoordinator(
+                kosmos.applicationCoroutineScope,
                 logger,
                 systemClock,
+                notifCollection,
                 headsUpManager,
                 headsUpViewBinder,
                 visualInterruptionDecisionProvider,
                 remoteInputManager,
                 launchFullScreenIntentProvider,
                 flags,
+                statusBarNotificationChipsInteractor,
                 headerController,
-                executor
+                executor,
             )
         coordinator.attach(notifPipeline)
 
@@ -351,7 +368,7 @@
         assertFalse(
             notifLifetimeExtender.maybeExtendLifetime(
                 NotificationEntryBuilder().setPkg("test-package").build(),
-                /* reason= */ 0
+                /* reason= */ 0,
             )
         )
     }
@@ -442,6 +459,97 @@
     }
 
     @Test
+    @EnableFlags(StatusBarNotifChips.FLAG_NAME)
+    fun showPromotedNotification_hasNotifEntry_shownAsHUN() =
+        testScope.runTest {
+            whenever(notifCollection.getEntry(entry.key)).thenReturn(entry)
+
+            statusBarNotificationChipsInteractor.onPromotedNotificationChipTapped(entry.key)
+            executor.advanceClockToLast()
+            executor.runAllReady()
+            beforeFinalizeFilterListener.onBeforeFinalizeFilter(listOf(entry))
+
+            finishBind(entry)
+            verify(headsUpManager).showNotification(entry)
+        }
+
+    @Test
+    @EnableFlags(StatusBarNotifChips.FLAG_NAME)
+    fun showPromotedNotification_noNotifEntry_noHUN() =
+        testScope.runTest {
+            whenever(notifCollection.getEntry(entry.key)).thenReturn(null)
+
+            statusBarNotificationChipsInteractor.onPromotedNotificationChipTapped(entry.key)
+            executor.advanceClockToLast()
+            executor.runAllReady()
+            beforeFinalizeFilterListener.onBeforeFinalizeFilter(listOf(entry))
+
+            verify(headsUpViewBinder, never()).bindHeadsUpView(eq(entry), any())
+            verify(headsUpManager, never()).showNotification(entry)
+        }
+
+    @Test
+    @EnableFlags(StatusBarNotifChips.FLAG_NAME)
+    fun showPromotedNotification_shownAsHUNEvenIfEntryShouldNot() =
+        testScope.runTest {
+            whenever(notifCollection.getEntry(entry.key)).thenReturn(entry)
+
+            // First, add the entry as shouldn't HUN
+            setShouldHeadsUp(entry, false)
+            collectionListener.onEntryAdded(entry)
+            beforeTransformGroupsListener.onBeforeTransformGroups(listOf(entry))
+            beforeFinalizeFilterListener.onBeforeFinalizeFilter(listOf(entry))
+
+            // WHEN that entry becomes a promoted notification and is tapped
+            statusBarNotificationChipsInteractor.onPromotedNotificationChipTapped(entry.key)
+            executor.advanceClockToLast()
+            executor.runAllReady()
+            beforeFinalizeFilterListener.onBeforeFinalizeFilter(listOf(entry))
+
+            // THEN it's still shown as heads up
+            finishBind(entry)
+            verify(headsUpManager).showNotification(entry)
+        }
+
+    @Test
+    @EnableFlags(StatusBarNotifChips.FLAG_NAME)
+    fun showPromotedNotification_atSameTimeAsOnAdded_promotedShownAsHUN() =
+        testScope.runTest {
+            // First, the promoted notification appears as not heads up
+            val promotedEntry = NotificationEntryBuilder().setPkg("promotedPackage").build()
+            whenever(notifCollection.getEntry(promotedEntry.key)).thenReturn(promotedEntry)
+            setShouldHeadsUp(promotedEntry, false)
+
+            collectionListener.onEntryAdded(promotedEntry)
+            beforeTransformGroupsListener.onBeforeTransformGroups(listOf(promotedEntry))
+            beforeFinalizeFilterListener.onBeforeFinalizeFilter(listOf(promotedEntry))
+
+            verify(headsUpViewBinder, never()).bindHeadsUpView(eq(promotedEntry), any())
+            verify(headsUpManager, never()).showNotification(promotedEntry)
+
+            // Then a new notification comes in that should be heads up
+            setShouldHeadsUp(entry, false)
+            whenever(notifCollection.getEntry(entry.key)).thenReturn(entry)
+            collectionListener.onEntryAdded(entry)
+
+            // At the same time, the promoted notification chip is tapped
+            statusBarNotificationChipsInteractor.onPromotedNotificationChipTapped(promotedEntry.key)
+            executor.advanceClockToLast()
+            executor.runAllReady()
+
+            // WHEN we finalize the pipeline
+            beforeTransformGroupsListener.onBeforeTransformGroups(listOf(promotedEntry, entry))
+            beforeFinalizeFilterListener.onBeforeFinalizeFilter(listOf(promotedEntry, entry))
+
+            // THEN the promoted entry is shown as a HUN, *not* the new entry
+            finishBind(promotedEntry)
+            verify(headsUpManager).showNotification(promotedEntry)
+
+            verify(headsUpViewBinder, never()).bindHeadsUpView(eq(entry), any())
+            verify(headsUpManager, never()).showNotification(entry)
+        }
+
+    @Test
     fun testTransferIsolatedChildAlert_withGroupAlertSummary() {
         setShouldHeadsUp(groupSummary)
         whenever(notifPipeline.allNotifs).thenReturn(listOf(groupSummary, groupSibling1))
@@ -862,7 +970,7 @@
         verify(launchFullScreenIntentProvider).launchFullScreenIntent(entry)
         verifyLoggedFullScreenIntentDecision(
             entry,
-            FullScreenIntentDecision.FSI_DEVICE_NOT_INTERACTIVE
+            FullScreenIntentDecision.FSI_DEVICE_NOT_INTERACTIVE,
         )
     }
 
@@ -885,7 +993,7 @@
         verify(launchFullScreenIntentProvider, never()).launchFullScreenIntent(any())
         verifyLoggedFullScreenIntentDecision(
             entry,
-            FullScreenIntentDecision.NO_FSI_SUPPRESSED_ONLY_BY_DND
+            FullScreenIntentDecision.NO_FSI_SUPPRESSED_ONLY_BY_DND,
         )
     }
 
@@ -899,7 +1007,7 @@
         verify(launchFullScreenIntentProvider, never()).launchFullScreenIntent(any())
         verifyLoggedFullScreenIntentDecision(
             entry,
-            FullScreenIntentDecision.NO_FSI_SUPPRESSED_ONLY_BY_DND
+            FullScreenIntentDecision.NO_FSI_SUPPRESSED_ONLY_BY_DND,
         )
         clearInterruptionProviderInvocations()
 
@@ -917,7 +1025,7 @@
         verify(headsUpManager, never()).showNotification(any())
         verifyLoggedFullScreenIntentDecision(
             entry,
-            FullScreenIntentDecision.FSI_DEVICE_NOT_INTERACTIVE
+            FullScreenIntentDecision.FSI_DEVICE_NOT_INTERACTIVE,
         )
         clearInterruptionProviderInvocations()
 
@@ -942,7 +1050,7 @@
         verify(launchFullScreenIntentProvider, never()).launchFullScreenIntent(any())
         verifyLoggedFullScreenIntentDecision(
             entry,
-            FullScreenIntentDecision.NO_FSI_SUPPRESSED_ONLY_BY_DND
+            FullScreenIntentDecision.NO_FSI_SUPPRESSED_ONLY_BY_DND,
         )
         clearInterruptionProviderInvocations()
 
@@ -975,7 +1083,7 @@
         verify(headsUpManager, never()).showNotification(any())
         verifyLoggedFullScreenIntentDecision(
             entry,
-            FullScreenIntentDecision.FSI_DEVICE_NOT_INTERACTIVE
+            FullScreenIntentDecision.FSI_DEVICE_NOT_INTERACTIVE,
         )
         clearInterruptionProviderInvocations()
     }
@@ -1070,7 +1178,7 @@
 
     private fun setShouldFullScreen(
         entry: NotificationEntry,
-        originalDecision: FullScreenIntentDecision
+        originalDecision: FullScreenIntentDecision,
     ) {
         whenever(visualInterruptionDecisionProvider.makeUnloggedFullScreenIntentDecision(entry))
             .thenAnswer { FullScreenIntentDecisionImpl(entry, originalDecision) }
@@ -1078,7 +1186,7 @@
 
     private fun verifyLoggedFullScreenIntentDecision(
         entry: NotificationEntry,
-        originalDecision: FullScreenIntentDecision
+        originalDecision: FullScreenIntentDecision,
     ) {
         val decision = withArgCaptor {
             verify(visualInterruptionDecisionProvider).logFullScreenIntentDecision(capture())
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterViewTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterViewTest.java
similarity index 88%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterViewTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterViewTest.java
index a21ca94..c9ca67e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterViewTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterViewTest.java
@@ -45,23 +45,25 @@
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.res.R;
 import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor;
+import com.android.systemui.statusbar.notification.footer.shared.NotifRedesignFooter;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.util.List;
+
 import platform.test.runner.parameterized.ParameterizedAndroidJunit4;
 import platform.test.runner.parameterized.Parameters;
 
-import java.util.List;
-
 @SmallTest
 @RunWith(ParameterizedAndroidJunit4.class)
 public class FooterViewTest extends SysuiTestCase {
 
     @Parameters(name = "{0}")
     public static List<FlagsParameterization> getFlags() {
-        return FlagsParameterization.allCombinationsOf(FooterViewRefactor.FLAG_NAME);
+        return FlagsParameterization.progressionOf(FooterViewRefactor.FLAG_NAME,
+                NotifRedesignFooter.FLAG_NAME);
     }
 
     public FooterViewTest(FlagsParameterization flags) {
@@ -74,8 +76,13 @@
 
     @Before
     public void setUp() {
-        mView = (FooterView) LayoutInflater.from(mSpyContext).inflate(
-                R.layout.status_bar_notification_footer, null, false);
+        if (NotifRedesignFooter.isEnabled()) {
+            mView = (FooterView) LayoutInflater.from(mSpyContext).inflate(
+                    R.layout.status_bar_notification_footer_redesign, null, false);
+        } else {
+            mView = (FooterView) LayoutInflater.from(mSpyContext).inflate(
+                    R.layout.status_bar_notification_footer, null, false);
+        }
         mView.setAnimationDuration(0);
     }
 
@@ -92,13 +99,14 @@
     }
 
     @Test
+    @DisableFlags(NotifRedesignFooter.FLAG_NAME)
     public void setManageOnClick() {
         mView.setManageButtonClickListener(mock(View.OnClickListener.class));
         assertTrue(mView.findViewById(R.id.manage_text).hasOnClickListeners());
     }
 
     @Test
-    @DisableFlags(FooterViewRefactor.FLAG_NAME)
+    @DisableFlags({FooterViewRefactor.FLAG_NAME, NotifRedesignFooter.FLAG_NAME})
     public void setHistoryShown() {
         mView.showHistory(true);
         assertTrue(mView.isHistoryShown());
@@ -107,7 +115,7 @@
     }
 
     @Test
-    @DisableFlags(FooterViewRefactor.FLAG_NAME)
+    @DisableFlags({FooterViewRefactor.FLAG_NAME, NotifRedesignFooter.FLAG_NAME})
     public void setHistoryNotShown() {
         mView.showHistory(false);
         assertFalse(mView.isHistoryShown());
@@ -133,6 +141,7 @@
 
     @Test
     @EnableFlags(FooterViewRefactor.FLAG_NAME)
+    @DisableFlags(NotifRedesignFooter.FLAG_NAME)
     public void testSetManageOrHistoryButtonText_resourceOnlyFetchedOnce() {
         int resId = R.string.manage_notifications_history_text;
         mView.setManageOrHistoryButtonText(resId);
@@ -151,7 +160,7 @@
     }
 
     @Test
-    @DisableFlags(FooterViewRefactor.FLAG_NAME)
+    @DisableFlags({FooterViewRefactor.FLAG_NAME, NotifRedesignFooter.FLAG_NAME})
     public void testSetManageOrHistoryButtonText_expectsFlagEnabled() {
         clearInvocations(mSpyContext);
         int resId = R.string.manage_notifications_history_text;
@@ -161,6 +170,7 @@
 
     @Test
     @EnableFlags(FooterViewRefactor.FLAG_NAME)
+    @DisableFlags(NotifRedesignFooter.FLAG_NAME)
     public void testSetManageOrHistoryButtonDescription_resourceOnlyFetchedOnce() {
         int resId = R.string.manage_notifications_history_text;
         mView.setManageOrHistoryButtonDescription(resId);
@@ -179,7 +189,7 @@
     }
 
     @Test
-    @DisableFlags(FooterViewRefactor.FLAG_NAME)
+    @DisableFlags({FooterViewRefactor.FLAG_NAME, NotifRedesignFooter.FLAG_NAME})
     public void testSetManageOrHistoryButtonDescription_expectsFlagEnabled() {
         clearInvocations(mSpyContext);
         int resId = R.string.accessibility_clear_all;
@@ -207,7 +217,7 @@
     }
 
     @Test
-    @DisableFlags(FooterViewRefactor.FLAG_NAME)
+    @DisableFlags({FooterViewRefactor.FLAG_NAME, NotifRedesignFooter.FLAG_NAME})
     public void testSetClearAllButtonText_expectsFlagEnabled() {
         clearInvocations(mSpyContext);
         int resId = R.string.clear_all_notifications_text;
@@ -235,7 +245,7 @@
     }
 
     @Test
-    @DisableFlags(FooterViewRefactor.FLAG_NAME)
+    @DisableFlags({FooterViewRefactor.FLAG_NAME, NotifRedesignFooter.FLAG_NAME})
     public void testSetClearAllButtonDescription_expectsFlagEnabled() {
         clearInvocations(mSpyContext);
         int resId = R.string.accessibility_clear_all;
@@ -263,7 +273,7 @@
     }
 
     @Test
-    @DisableFlags(FooterViewRefactor.FLAG_NAME)
+    @DisableFlags({FooterViewRefactor.FLAG_NAME, NotifRedesignFooter.FLAG_NAME})
     public void testSetMessageString_expectsFlagEnabled() {
         clearInvocations(mSpyContext);
         int resId = R.string.unlock_to_see_notif_text;
@@ -288,7 +298,7 @@
     }
 
     @Test
-    @DisableFlags(FooterViewRefactor.FLAG_NAME)
+    @DisableFlags({FooterViewRefactor.FLAG_NAME, NotifRedesignFooter.FLAG_NAME})
     public void testSetMessageIcon_expectsFlagEnabled() {
         clearInvocations(mSpyContext);
         int resId = R.drawable.ic_friction_lock_closed;
@@ -310,4 +320,3 @@
                 .isEqualTo(View.GONE);
     }
 }
-
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModelTest.kt
index 46f3a6b..1adfc2b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModelTest.kt
@@ -18,6 +18,7 @@
 
 package com.android.systemui.statusbar.notification.footer.ui.viewmodel
 
+import android.platform.test.annotations.DisableFlags
 import android.platform.test.annotations.EnableFlags
 import android.platform.test.flag.junit.FlagsParameterization
 import android.provider.Settings
@@ -40,6 +41,7 @@
 import com.android.systemui.statusbar.notification.data.repository.activeNotificationListRepository
 import com.android.systemui.statusbar.notification.emptyshade.shared.ModesEmptyShadeFix
 import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor
+import com.android.systemui.statusbar.notification.footer.shared.NotifRedesignFooter
 import com.android.systemui.testKosmos
 import com.android.systemui.util.ui.isAnimating
 import com.android.systemui.util.ui.value
@@ -230,6 +232,7 @@
         }
 
     @Test
+    @DisableFlags(NotifRedesignFooter.FLAG_NAME)
     fun manageButton_whenHistoryDisabled() =
         testScope.runTest {
             val buttonLabel by collectLastValue(underTest.manageOrHistoryButton.labelId)
@@ -243,6 +246,7 @@
         }
 
     @Test
+    @DisableFlags(NotifRedesignFooter.FLAG_NAME)
     fun historyButton_whenHistoryEnabled() =
         testScope.runTest {
             val buttonLabel by collectLastValue(underTest.manageOrHistoryButton.labelId)
@@ -255,8 +259,9 @@
             assertThat(buttonLabel).isEqualTo(R.string.manage_notifications_history_text)
         }
 
-    @EnableFlags(ModesEmptyShadeFix.FLAG_NAME)
     @Test
+    @EnableFlags(ModesEmptyShadeFix.FLAG_NAME)
+    @DisableFlags(NotifRedesignFooter.FLAG_NAME)
     fun manageButtonOnClick_whenHistoryDisabled() =
         testScope.runTest {
             val onClick by collectLastValue(underTest.manageOrHistoryButtonClick)
@@ -271,8 +276,9 @@
             assertThat(onClick?.backStack).isEmpty()
         }
 
-    @EnableFlags(ModesEmptyShadeFix.FLAG_NAME)
     @Test
+    @EnableFlags(ModesEmptyShadeFix.FLAG_NAME)
+    @DisableFlags(NotifRedesignFooter.FLAG_NAME)
     fun historyButtonOnClick_whenHistoryEnabled() =
         testScope.runTest {
             val onClick by collectLastValue(underTest.manageOrHistoryButtonClick)
@@ -289,6 +295,7 @@
         }
 
     @Test
+    @DisableFlags(NotifRedesignFooter.FLAG_NAME)
     fun manageButtonVisible_whenMessageVisible() =
         testScope.runTest {
             val visible by collectLastValue(underTest.manageOrHistoryButton.isVisible)
@@ -299,6 +306,7 @@
         }
 
     @Test
+    @DisableFlags(NotifRedesignFooter.FLAG_NAME)
     fun manageButtonVisible_whenMessageNotVisible() =
         testScope.runTest {
             val visible by collectLastValue(underTest.manageOrHistoryButton.isVisible)
@@ -307,4 +315,30 @@
 
             assertThat(visible?.value).isTrue()
         }
+
+    @Test
+    @EnableFlags(NotifRedesignFooter.FLAG_NAME)
+    fun settingsAndHistoryButtonsNotVisible_whenMessageVisible() =
+        testScope.runTest {
+            val settingsVisible by collectLastValue(underTest.settingsButtonVisible)
+            val historyVisible by collectLastValue(underTest.historyButtonVisible)
+
+            activeNotificationListRepository.hasFilteredOutSeenNotifications.value = true
+
+            assertThat(settingsVisible).isFalse()
+            assertThat(historyVisible).isFalse()
+        }
+
+    @Test
+    @EnableFlags(NotifRedesignFooter.FLAG_NAME)
+    fun settingsAndHistoryButtonsNotVisible_whenMessageNotVisible() =
+        testScope.runTest {
+            val settingsVisible by collectLastValue(underTest.settingsButtonVisible)
+            val historyVisible by collectLastValue(underTest.historyButtonVisible)
+
+            activeNotificationListRepository.hasFilteredOutSeenNotifications.value = false
+
+            assertThat(settingsVisible).isTrue()
+            assertThat(historyVisible).isTrue()
+        }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
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 25670cb..d5a7c89 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
@@ -64,7 +64,6 @@
 import com.android.systemui.scene.data.repository.Transition
 import com.android.systemui.scene.data.repository.setTransition
 import com.android.systemui.scene.shared.model.Scenes
-import com.android.systemui.shade.data.repository.fakeShadeRepository
 import com.android.systemui.shade.mockLargeScreenHeaderHelper
 import com.android.systemui.shade.shadeTestUtil
 import com.android.systemui.statusbar.notification.stack.domain.interactor.sharedNotificationContainerInteractor
@@ -116,36 +115,18 @@
         kosmos.aodBurnInViewModel = aodBurnInViewModel
     }
 
-    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
-
-    val communalSceneRepository
-        get() = kosmos.communalSceneRepository
-
-    val shadeRepository
-        get() = kosmos.fakeShadeRepository
+    private val testScope = kosmos.testScope
+    private val configurationRepository by lazy { kosmos.fakeConfigurationRepository }
+    private val keyguardRepository by lazy { kosmos.fakeKeyguardRepository }
+    private val keyguardInteractor by lazy { kosmos.keyguardInteractor }
+    private val keyguardRootViewModel by lazy { kosmos.keyguardRootViewModel }
+    private val keyguardTransitionRepository by lazy { kosmos.fakeKeyguardTransitionRepository }
+    private val shadeTestUtil by lazy { kosmos.shadeTestUtil }
+    private val sharedNotificationContainerInteractor by lazy {
+        kosmos.sharedNotificationContainerInteractor
+    }
+    private val largeScreenHeaderHelper by lazy { kosmos.mockLargeScreenHeaderHelper }
+    private val communalSceneRepository by lazy { kosmos.communalSceneRepository }
 
     lateinit var underTest: SharedNotificationContainerViewModel
 
@@ -637,6 +618,45 @@
 
     @Test
     @DisableSceneContainer
+    fun boundsStableWhenGoingToAlternateBouncer() =
+        testScope.runTest {
+            val bounds by collectLastValue(underTest.bounds)
+
+            // Start on lockscreen
+            showLockscreen()
+
+            keyguardInteractor.setNotificationContainerBounds(
+                NotificationContainerBounds(top = 1f, bottom = 2f)
+            )
+
+            assertThat(bounds).isEqualTo(NotificationContainerBounds(top = 1f, bottom = 2f))
+
+            // Begin transition to AOD
+            keyguardTransitionRepository.sendTransitionStep(
+                TransitionStep(LOCKSCREEN, ALTERNATE_BOUNCER, 0f, TransitionState.STARTED)
+            )
+            runCurrent()
+            keyguardTransitionRepository.sendTransitionStep(
+                TransitionStep(LOCKSCREEN, ALTERNATE_BOUNCER, 0f, TransitionState.RUNNING)
+            )
+            runCurrent()
+
+            // This is the last step before FINISHED is sent, which could trigger a change in bounds
+            keyguardTransitionRepository.sendTransitionStep(
+                TransitionStep(LOCKSCREEN, ALTERNATE_BOUNCER, 1f, TransitionState.RUNNING)
+            )
+            runCurrent()
+            assertThat(bounds).isEqualTo(NotificationContainerBounds(top = 1f, bottom = 2f))
+
+            keyguardTransitionRepository.sendTransitionStep(
+                TransitionStep(LOCKSCREEN, ALTERNATE_BOUNCER, 1f, TransitionState.FINISHED)
+            )
+            runCurrent()
+            assertThat(bounds).isEqualTo(NotificationContainerBounds(top = 1f, bottom = 2f))
+        }
+
+    @Test
+    @DisableSceneContainer
     fun boundsDoNotChangeWhileLockscreenToAodTransitionIsActive() =
         testScope.runTest {
             val bounds by collectLastValue(underTest.bounds)
@@ -1229,6 +1249,75 @@
             assertThat(alpha).isEqualTo(1f)
         }
 
+    @Test
+    @DisableSceneContainer
+    fun notificationAbsoluteBottom() =
+        testScope.runTest {
+            var notificationCount = 2
+            val calculateSpace = { _: Float, _: Boolean -> notificationCount }
+            val shelfHeight = 10F
+            val heightForNotification = 20F
+            val calculateHeight = { count: Int -> count * heightForNotification + shelfHeight }
+            val stackAbsoluteBottom by
+                collectLastValue(
+                    underTest.getNotificationStackAbsoluteBottom(
+                        calculateSpace,
+                        calculateHeight,
+                        shelfHeight,
+                    )
+                )
+            advanceTimeBy(50L)
+            showLockscreen()
+
+            shadeTestUtil.setSplitShade(false)
+            keyguardInteractor.setNotificationContainerBounds(
+                NotificationContainerBounds(top = 100F, bottom = 300F)
+            )
+            configurationRepository.onAnyConfigurationChange()
+
+            assertThat(stackAbsoluteBottom).isEqualTo(150F)
+
+            // Also updates when directly requested (as it would from NotificationStackScrollLayout)
+            notificationCount = 3
+            sharedNotificationContainerInteractor.notificationStackChanged()
+            advanceTimeBy(50L)
+            assertThat(stackAbsoluteBottom).isEqualTo(170F)
+        }
+
+    @Test
+    @DisableSceneContainer
+    fun notificationAbsoluteBottom_maxNotificationIsZero_noShelfHeight() =
+        testScope.runTest {
+            var notificationCount = 2
+            val calculateSpace = { _: Float, _: Boolean -> notificationCount }
+            val shelfHeight = 10F
+            val heightForNotification = 20F
+            val calculateHeight = { count: Int -> count * heightForNotification + shelfHeight }
+            val stackAbsoluteBottom by
+                collectLastValue(
+                    underTest.getNotificationStackAbsoluteBottom(
+                        calculateSpace,
+                        calculateHeight,
+                        shelfHeight,
+                    )
+                )
+            advanceTimeBy(50L)
+            showLockscreen()
+
+            shadeTestUtil.setSplitShade(false)
+            keyguardInteractor.setNotificationContainerBounds(
+                NotificationContainerBounds(top = 100F, bottom = 300F)
+            )
+            configurationRepository.onAnyConfigurationChange()
+
+            assertThat(stackAbsoluteBottom).isEqualTo(150F)
+
+            notificationCount = 0
+            sharedNotificationContainerInteractor.notificationStackChanged()
+            advanceTimeBy(50L)
+            assertThat(stackAbsoluteBottom).isEqualTo(100F)
+        }
+
     private suspend fun TestScope.showLockscreen() {
         shadeTestUtil.setQsExpansion(0f)
         shadeTestUtil.setLockscreenShadeExpansion(0f)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
index 21a317a..b2794d8 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
@@ -71,6 +71,7 @@
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor;
 import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor;
+import com.android.systemui.bouncer.domain.interactor.BouncerInteractor;
 import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerCallbackInteractor;
 import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerCallbackInteractor.PrimaryBouncerExpansionCallback;
 import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor;
@@ -82,8 +83,8 @@
 import com.android.systemui.flags.DisableSceneContainer;
 import com.android.systemui.flags.EnableSceneContainer;
 import com.android.systemui.keyguard.DismissCallbackRegistry;
-import com.android.systemui.keyguard.domain.interactor.KeyguardDismissTransitionInteractor;
 import com.android.systemui.keyguard.domain.interactor.KeyguardDismissActionInteractor;
+import com.android.systemui.keyguard.domain.interactor.KeyguardDismissTransitionInteractor;
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
 import com.android.systemui.keyguard.shared.model.KeyguardState;
 import com.android.systemui.keyguard.shared.model.TransitionState;
@@ -170,6 +171,7 @@
     @Mock private DeviceEntryInteractor mDeviceEntryInteractor;
     @Mock private SceneInteractor mSceneInteractor;
     @Mock private DismissCallbackRegistry mDismissCallbackRegistry;
+    @Mock private BouncerInteractor mBouncerInteractor;
 
     private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
     private PrimaryBouncerCallbackInteractor.PrimaryBouncerExpansionCallback
@@ -241,7 +243,8 @@
                         mock(StatusBarKeyguardViewManagerInteractor.class),
                         mExecutor,
                         () -> mDeviceEntryInteractor,
-                        mDismissCallbackRegistry) {
+                        mDismissCallbackRegistry,
+                        () -> mBouncerInteractor) {
                     @Override
                     public ViewRootImpl getViewRootImpl() {
                         return mViewRootImpl;
@@ -748,7 +751,8 @@
                         mock(StatusBarKeyguardViewManagerInteractor.class),
                         mExecutor,
                         () -> mDeviceEntryInteractor,
-                        mDismissCallbackRegistry) {
+                        mDismissCallbackRegistry,
+                        () -> mBouncerInteractor) {
                     @Override
                     public ViewRootImpl getViewRootImpl() {
                         return mViewRootImpl;
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallbackTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallbackTest.java
index 81c40dc..8ec17da 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallbackTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallbackTest.java
@@ -261,6 +261,7 @@
         when(enr.getPrivateLayout()).thenReturn(privateLayout);
         when(enr.getEntry()).thenReturn(enrEntry);
         when(enr.isChildInGroup()).thenReturn(false);
+        when(enr.isPinned()).thenReturn(false);
         when(enr.isExpanded()).thenReturn(false);
 
         // WHEN
@@ -287,6 +288,7 @@
         when(enr.getPrivateLayout()).thenReturn(privateLayout);
         when(enr.getEntry()).thenReturn(enrEntry);
         when(enr.isChildInGroup()).thenReturn(false);
+        when(enr.isPinned()).thenReturn(false);
         when(enr.isExpanded()).thenReturn(true);
 
         // WHEN
@@ -299,4 +301,58 @@
         verify(enr, never()).setUserExpanded(anyBoolean());
         verify(mGroupExpansionManager, never()).toggleGroupExpansion(any());
     }
+
+    @Test
+    @EnableFlags(ExpandHeadsUpOnInlineReply.FLAG_NAME)
+    public void onMakeExpandedVisibleForRemoteInput_notExpandedPinnedHUN_toggleExpansion() {
+        // GIVEN
+        final Runnable onExpandedVisibleRunner = mock(Runnable.class);
+
+        final ExpandableNotificationRow enr = mock(ExpandableNotificationRow.class);
+        final NotificationContentView privateLayout = mock(NotificationContentView.class);
+        final NotificationEntry enrEntry = mock(NotificationEntry.class);
+
+        when(enr.getPrivateLayout()).thenReturn(privateLayout);
+        when(enr.getEntry()).thenReturn(enrEntry);
+        when(enr.isChildInGroup()).thenReturn(false);
+        when(enr.isPinned()).thenReturn(true);
+        when(enr.isPinnedAndExpanded()).thenReturn(false);
+
+        // WHEN
+        mRemoteInputCallback.onMakeExpandedVisibleForRemoteInput(
+                enr, mock(View.class), false, onExpandedVisibleRunner);
+
+        // THEN
+        verify(enr).toggleExpansionState();
+        verify(privateLayout).setOnExpandedVisibleListener(onExpandedVisibleRunner);
+        verify(enr, never()).setUserExpanded(anyBoolean());
+        verify(mGroupExpansionManager, never()).toggleGroupExpansion(any());
+    }
+
+    @Test
+    @EnableFlags(ExpandHeadsUpOnInlineReply.FLAG_NAME)
+    public void onMakeExpandedVisibleForRemoteInput_expandedPinnedHUN_notToggleExpansion() {
+        // GIVEN
+        final Runnable onExpandedVisibleRunner = mock(Runnable.class);
+
+        final ExpandableNotificationRow enr = mock(ExpandableNotificationRow.class);
+        final NotificationContentView privateLayout = mock(NotificationContentView.class);
+        final NotificationEntry enrEntry = mock(NotificationEntry.class);
+
+        when(enr.getPrivateLayout()).thenReturn(privateLayout);
+        when(enr.getEntry()).thenReturn(enrEntry);
+        when(enr.isChildInGroup()).thenReturn(false);
+        when(enr.isPinned()).thenReturn(true);
+        when(enr.isPinnedAndExpanded()).thenReturn(true);
+
+        // WHEN
+        mRemoteInputCallback.onMakeExpandedVisibleForRemoteInput(
+                enr, mock(View.class), false, onExpandedVisibleRunner);
+
+        // THEN
+        verify(enr, never()).toggleExpansionState();
+        verify(privateLayout, never()).setOnExpandedVisibleListener(onExpandedVisibleRunner);
+        verify(enr, never()).setUserExpanded(anyBoolean());
+        verify(mGroupExpansionManager, never()).toggleGroupExpansion(any());
+    }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/ui/model/SignalIconModelParameterizedTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/ui/model/SignalIconModelParameterizedTest.kt
index ebec003..3d76033 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/ui/model/SignalIconModelParameterizedTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/ui/model/SignalIconModelParameterizedTest.kt
@@ -21,13 +21,13 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.statusbar.pipeline.mobile.domain.model.SignalIconModel
 import com.google.common.truth.Truth.assertThat
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.junit.runners.Parameterized
-import org.junit.runners.Parameterized.Parameters
 
 @SmallTest
-@RunWith(Parameterized::class)
+@RunWith(ParameterizedAndroidJunit4::class)
 internal class SignalIconModelParameterizedTest(private val testCase: TestCase) : SysuiTestCase() {
     @Test
     fun drawableFromModel_level0_numLevels4_noExclamation_notCarrierNetworkChange() {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/BluetoothControllerImplTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/BluetoothControllerImplTest.java
index e3bd885..4557182 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/BluetoothControllerImplTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/BluetoothControllerImplTest.java
@@ -30,7 +30,6 @@
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothDevice;
 import android.bluetooth.BluetoothProfile;
-import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.testing.TestableLooper.RunWithLooper;
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/CastDeviceTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/CastDeviceTest.kt
index 16061df..1b7b47f 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/CastDeviceTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/CastDeviceTest.kt
@@ -23,6 +23,7 @@
 import android.content.pm.ResolveInfo
 import android.media.MediaRouter
 import android.media.projection.MediaProjectionInfo
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.log.logcatLogBuffer
@@ -30,6 +31,7 @@
 import com.android.systemui.statusbar.policy.CastDevice.Companion.toCastDevice
 import com.google.common.truth.Truth.assertThat
 import kotlin.test.Test
+import org.junit.runner.RunWith
 import org.mockito.ArgumentMatchers
 import org.mockito.Mockito.doAnswer
 import org.mockito.kotlin.any
@@ -38,6 +40,7 @@
 import org.mockito.kotlin.whenever
 
 @SmallTest
+@RunWith(AndroidJUnit4::class)
 class CastDeviceTest : SysuiTestCase() {
     private val mockAppInfo =
         mock<ApplicationInfo>().apply { whenever(this.loadLabel(any())).thenReturn("") }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RotationLockControllerImplTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/RotationLockControllerImplTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RotationLockControllerImplTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/RotationLockControllerImplTest.java
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt
index 1af0f79..b03c679 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt
@@ -24,14 +24,15 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.kosmos.unconfinedTestDispatcher
-import com.android.systemui.kosmos.unconfinedTestScope
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.kosmos.useUnconfinedTestDispatcher
 import com.android.systemui.settings.FakeUserTracker
 import com.android.systemui.testKosmos
 import com.android.systemui.user.data.model.SelectedUserModel
 import com.android.systemui.user.data.model.SelectionStatus
 import com.android.systemui.user.data.model.UserSwitcherSettingsModel
-import com.android.systemui.util.settings.unconfinedDispatcherFakeGlobalSettings
+import com.android.systemui.util.settings.fakeGlobalSettings
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Dispatchers
@@ -52,10 +53,10 @@
 @RunWith(AndroidJUnit4::class)
 class UserRepositoryImplTest : SysuiTestCase() {
 
-    private val kosmos = testKosmos()
-    private val testDispatcher = kosmos.unconfinedTestDispatcher
-    private val testScope = kosmos.unconfinedTestScope
-    private val globalSettings = kosmos.unconfinedDispatcherFakeGlobalSettings
+    private val kosmos = testKosmos().useUnconfinedTestDispatcher()
+    private val testDispatcher = kosmos.testDispatcher
+    private val testScope = kosmos.testScope
+    private val globalSettings = kosmos.fakeGlobalSettings
 
     @Mock private lateinit var manager: UserManager
 
@@ -131,11 +132,7 @@
             whenever(mainUser.identifier).thenReturn(mainUserId)
 
             underTest = create(testScope.backgroundScope)
-            val initialExpectedValue =
-                setUpUsers(
-                    count = 3,
-                    selectedIndex = 0,
-                )
+            val initialExpectedValue = setUpUsers(count = 3, selectedIndex = 0)
             var userInfos: List<UserInfo>? = null
             var selectedUserInfo: UserInfo? = null
             val job1 = underTest.userInfos.onEach { userInfos = it }.launchIn(this)
@@ -146,11 +143,7 @@
             assertThat(selectedUserInfo).isEqualTo(initialExpectedValue[0])
             assertThat(underTest.lastSelectedNonGuestUserId).isEqualTo(selectedUserInfo?.id)
 
-            val secondExpectedValue =
-                setUpUsers(
-                    count = 4,
-                    selectedIndex = 1,
-                )
+            val secondExpectedValue = setUpUsers(count = 4, selectedIndex = 1)
             underTest.refreshUsers()
             assertThat(userInfos).isEqualTo(secondExpectedValue)
             assertThat(selectedUserInfo).isEqualTo(secondExpectedValue[1])
@@ -158,11 +151,7 @@
 
             val selectedNonGuestUserId = selectedUserInfo?.id
             val thirdExpectedValue =
-                setUpUsers(
-                    count = 2,
-                    isLastGuestUser = true,
-                    selectedIndex = 1,
-                )
+                setUpUsers(count = 2, isLastGuestUser = true, selectedIndex = 1)
             underTest.refreshUsers()
             assertThat(userInfos).isEqualTo(thirdExpectedValue)
             assertThat(selectedUserInfo).isEqualTo(thirdExpectedValue[1])
@@ -177,12 +166,7 @@
     fun refreshUsers_sortsByCreationTime_guestUserLast() =
         testScope.runTest {
             underTest = create(testScope.backgroundScope)
-            val unsortedUsers =
-                setUpUsers(
-                    count = 3,
-                    selectedIndex = 0,
-                    isLastGuestUser = true,
-                )
+            val unsortedUsers = setUpUsers(count = 3, selectedIndex = 0, isLastGuestUser = true)
             unsortedUsers[0].creationTime = 999
             unsortedUsers[1].creationTime = 900
             unsortedUsers[2].creationTime = 950
@@ -207,10 +191,7 @@
     ): List<UserInfo> {
         val userInfos =
             (0 until count).map { index ->
-                createUserInfo(
-                    index,
-                    isGuest = isLastGuestUser && index == count - 1,
-                )
+                createUserInfo(index, isGuest = isLastGuestUser && index == count - 1)
             }
         whenever(manager.aliveUsers).thenReturn(userInfos)
         tracker.set(userInfos, selectedIndex)
@@ -224,16 +205,10 @@
             var selectedUserInfo: UserInfo? = null
             val job = underTest.selectedUserInfo.onEach { selectedUserInfo = it }.launchIn(this)
 
-            setUpUsers(
-                count = 2,
-                selectedIndex = 0,
-            )
+            setUpUsers(count = 2, selectedIndex = 0)
             tracker.onProfileChanged()
             assertThat(selectedUserInfo?.id).isEqualTo(0)
-            setUpUsers(
-                count = 2,
-                selectedIndex = 1,
-            )
+            setUpUsers(count = 2, selectedIndex = 1)
             tracker.onProfileChanged()
             assertThat(selectedUserInfo?.id).isEqualTo(1)
             job.cancel()
@@ -287,10 +262,7 @@
             job.cancel()
         }
 
-    private fun createUserInfo(
-        id: Int,
-        isGuest: Boolean,
-    ): UserInfo {
+    private fun createUserInfo(id: Int, isGuest: Boolean): UserInfo {
         val flags = 0
         return UserInfo(
             id,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/util/ConvenienceExtensionsKtTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/util/ConvenienceExtensionsKtTest.kt
new file mode 100644
index 0000000..2d57e2f
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/util/ConvenienceExtensionsKtTest.kt
@@ -0,0 +1,64 @@
+/*
+ * 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.util
+
+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 org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class ConvenienceExtensionsKtTest : SysuiTestCase() {
+
+    @Test
+    fun containsExactly_notDuplicatedElements_allSame_returnsTrue() {
+        val list = listOf(1, 2, 3)
+
+        assertThat(list.containsExactly(2, 1, 3)).isTrue()
+    }
+
+    @Test
+    fun containsExactly_duplicatedElements_allSame_returnsTrue() {
+        val list = listOf(1, 1, 2, 3, 3)
+
+        assertThat(list.containsExactly(1, 1, 2, 3, 3)).isTrue()
+    }
+
+    @Test
+    fun containsExactly_duplicatedElements_sameButNotDuplicated_returnsFalse() {
+        val list = listOf(1, 1, 2, 3, 3)
+
+        assertThat(list.containsExactly(1, 2, 3)).isFalse()
+    }
+
+    @Test
+    fun containsExactly_duplicatedElements_sameButNotSameAmount_returnsFalse() {
+        val list = listOf(1, 1, 2, 3, 3)
+
+        assertThat(list.containsExactly(1, 2, 2, 3, 3)).isFalse()
+    }
+
+    @Test
+    fun eachCountMap_returnsExpectedCount() {
+        val list = listOf(1, 3, 1, 3, 3, 3, 2)
+
+        assertThat(list.eachCountMap()).isEqualTo(mapOf(1 to 2, 2 to 1, 3 to 4))
+    }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java
index beba0f0..1914867 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java
@@ -172,7 +172,7 @@
         mVolumeController.setDeviceInteractive(false);
         when(mWakefullnessLifcycle.getWakefulness()).thenReturn(
                 WakefulnessLifecycle.WAKEFULNESS_AWAKE);
-        mVolumeController.onVolumeChangedW(0, AudioManager.FLAG_SHOW_UI);
+        mVolumeController.onVolumeChangedW(0, AudioManager.FLAG_SHOW_UI, true);
         verify(mCallback, never()).onShowRequested(Events.SHOW_REASON_VOLUME_CHANGED, false,
                 LOCK_TASK_MODE_NONE);
     }
@@ -182,7 +182,7 @@
         mVolumeController.setDeviceInteractive(true);
         when(mWakefullnessLifcycle.getWakefulness()).thenReturn(
                 WakefulnessLifecycle.WAKEFULNESS_AWAKE);
-        mVolumeController.onVolumeChangedW(0, AudioManager.FLAG_SHOW_UI);
+        mVolumeController.onVolumeChangedW(0, AudioManager.FLAG_SHOW_UI, true);
         verify(mCallback, times(1)).onShowRequested(Events.SHOW_REASON_VOLUME_CHANGED, false,
                 LOCK_TASK_MODE_NONE);
     }
@@ -192,11 +192,11 @@
         mVolumeController.setDeviceInteractive(true);
         when(mWakefullnessLifcycle.getWakefulness()).thenReturn(
                 WakefulnessLifecycle.WAKEFULNESS_AWAKE);
-        mVolumeController.onVolumeChangedW(0, AudioManager.FLAG_SHOW_UI);
+        mVolumeController.onVolumeChangedW(0, AudioManager.FLAG_SHOW_UI, true);
         mVolumeController.setDeviceInteractive(false);
         when(mWakefullnessLifcycle.getWakefulness()).thenReturn(
                 WakefulnessLifecycle.WAKEFULNESS_GOING_TO_SLEEP);
-        mVolumeController.onVolumeChangedW(0, AudioManager.FLAG_SHOW_UI);
+        mVolumeController.onVolumeChangedW(0, AudioManager.FLAG_SHOW_UI, true);
         verify(mCallback, times(1)).onShowRequested(Events.SHOW_REASON_VOLUME_CHANGED, false,
                 LOCK_TASK_MODE_NONE);
     }
@@ -210,7 +210,7 @@
                 AudioManager.DEVICE_OUT_BLE_HEADSET);
 
         mVolumeController.onVolumeChangedW(
-                AudioManager.STREAM_VOICE_CALL, AudioManager.FLAG_SHOW_UI);
+                AudioManager.STREAM_VOICE_CALL, AudioManager.FLAG_SHOW_UI, true);
 
         verify(mCallback, times(1)).onStateChanged(any());
     }
@@ -224,7 +224,7 @@
                 AudioManager.DEVICE_OUT_BLUETOOTH_A2DP);
 
         mVolumeController.onVolumeChangedW(
-                AudioManager.STREAM_VOICE_CALL, AudioManager.FLAG_SHOW_UI);
+                AudioManager.STREAM_VOICE_CALL, AudioManager.FLAG_SHOW_UI, true);
 
         verify(mCallback, never()).onStateChanged(any());
     }
@@ -241,14 +241,16 @@
                 .thenReturn(AudioManager.DEVICE_NONE);
 
         mVolumeController.mInAudioSharing = true;
-        mVolumeController.onVolumeChangedW(AudioManager.STREAM_MUSIC, AudioManager.FLAG_SHOW_UI);
+        mVolumeController.onVolumeChangedW(
+                AudioManager.STREAM_MUSIC, AudioManager.FLAG_SHOW_UI, true);
         verify(mCallback).onStateChanged(stateCaptor.capture());
         assertThat(stateCaptor.getValue().states.contains(AudioManager.STREAM_MUSIC)).isTrue();
         assertThat(stateCaptor.getValue().states.get(AudioManager.STREAM_MUSIC).routedToBluetooth)
                 .isTrue();
 
         mVolumeController.mInAudioSharing = false;
-        mVolumeController.onVolumeChangedW(AudioManager.STREAM_MUSIC, AudioManager.FLAG_SHOW_UI);
+        mVolumeController.onVolumeChangedW(
+                AudioManager.STREAM_MUSIC, AudioManager.FLAG_SHOW_UI, true);
         verify(mCallback, times(2)).onStateChanged(stateCaptor.capture());
         assertThat(stateCaptor.getValue().states.contains(AudioManager.STREAM_MUSIC)).isTrue();
         assertThat(stateCaptor.getValue().states.get(AudioManager.STREAM_MUSIC).routedToBluetooth)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/VolumeDialogControllerImplTestKt.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/VolumeDialogControllerImplTestKt.kt
index 98cea9d..76b7b8f 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/VolumeDialogControllerImplTestKt.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/VolumeDialogControllerImplTestKt.kt
@@ -18,12 +18,15 @@
 
 import android.app.activityManager
 import android.app.keyguardManager
+import android.content.Intent
 import android.content.applicationContext
 import android.content.packageManager
+import android.content.testableContext
 import android.media.AudioManager
 import android.media.IVolumeController
 import android.os.Handler
 import android.os.looper
+import android.os.testableLooper
 import android.platform.test.annotations.DisableFlags
 import android.platform.test.annotations.EnableFlags
 import android.platform.test.flag.junit.SetFlagsRule
@@ -34,6 +37,8 @@
 import com.android.settingslib.volume.data.model.VolumeControllerEvent
 import com.android.systemui.Flags
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.broadcast.broadcastDispatcher
+import com.android.systemui.broadcast.broadcastDispatcherContext
 import com.android.systemui.dump.dumpManager
 import com.android.systemui.keyguard.WakefulnessLifecycle
 import com.android.systemui.keyguard.wakefulnessLifecycle
@@ -81,10 +86,11 @@
             audioRepository.init()
             threadFactory =
                 FakeThreadFactory(FakeExecutor(fakeSystemClock)).apply { setLooper(looper) }
+            broadcastDispatcherContext = testableContext
             underTest =
                 VolumeDialogControllerImpl(
                         applicationContext,
-                        mock {},
+                        broadcastDispatcher,
                         mock {
                             on { ringerMode }.thenReturn(mock<RingerModeLiveData> {})
                             on { ringerModeInternal }.thenReturn(mock<RingerModeLiveData> {})
@@ -112,6 +118,23 @@
         }
 
     @Test
+    fun broadcastEvent_sendsChangesOnce() =
+        with(kosmos) {
+            testScope.runTest {
+                whenever(audioManager.getLastAudibleStreamVolume(any())).thenReturn(1)
+                broadcastDispatcher.sendIntentToMatchingReceiversOnly(
+                    applicationContext,
+                    Intent(AudioManager.ACTION_VOLUME_CHANGED).apply {
+                        putExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, AudioManager.STREAM_SYSTEM)
+                    },
+                )
+                testableLooper.processAllMessages()
+
+                verify(callbacks) { 1 * { onStateChanged(any()) } }
+            }
+        }
+
+    @Test
     @EnableFlags(Flags.FLAG_USE_VOLUME_CONTROLLER)
     fun useVolumeControllerEnabled_listensToVolumeController() =
         testVolumeController { stream: Int, flags: Int ->
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/dialog/ringer/ui/viewmodel/VolumeDialogRingerDrawerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/dialog/ringer/ui/viewmodel/VolumeDialogRingerDrawerViewModelTest.kt
new file mode 100644
index 0000000..1e6e52a
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/dialog/ringer/ui/viewmodel/VolumeDialogRingerDrawerViewModelTest.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.systemui.volume.dialog.ringer.ui.viewmodel
+
+import android.media.AudioManager.RINGER_MODE_NORMAL
+import android.media.AudioManager.RINGER_MODE_SILENT
+import android.media.AudioManager.RINGER_MODE_VIBRATE
+import android.media.AudioManager.STREAM_RING
+import android.testing.TestableLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.settingslib.volume.shared.model.RingerMode
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.haptics.fakeVibratorHelper
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.plugins.fakeVolumeDialogController
+import com.android.systemui.testKosmos
+import com.android.systemui.volume.data.repository.audioSystemRepository
+import com.google.common.truth.Truth.assertThat
+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.Test
+import org.junit.runner.RunWith
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+@TestableLooper.RunWithLooper
+class VolumeDialogRingerDrawerViewModelTest : SysuiTestCase() {
+
+    private val kosmos = testKosmos()
+    private val testScope = kosmos.testScope
+    private val controller = kosmos.fakeVolumeDialogController
+    private val vibratorHelper = kosmos.fakeVibratorHelper
+
+    private lateinit var underTest: VolumeDialogRingerDrawerViewModel
+
+    @Before
+    fun setUp() {
+        underTest = kosmos.volumeDialogRingerDrawerViewModel
+    }
+
+    @Test
+    fun onSelectedRingerNormalModeButtonClicked_openDrawer() =
+        testScope.runTest {
+            val ringerViewModel by collectLastValue(underTest.ringerViewModel)
+            val normalRingerMode = RingerMode(RINGER_MODE_NORMAL)
+
+            setUpRingerModeAndOpenDrawer(normalRingerMode)
+
+            assertThat(ringerViewModel).isInstanceOf(RingerViewModelState.Available::class.java)
+            assertThat((ringerViewModel as RingerViewModelState.Available).uiModel.drawerState)
+                .isEqualTo(RingerDrawerState.Open(normalRingerMode))
+        }
+
+    @Test
+    fun onSelectedRingerButtonClicked_drawerOpened_closeDrawer() =
+        testScope.runTest {
+            val ringerViewModel by collectLastValue(underTest.ringerViewModel)
+            val normalRingerMode = RingerMode(RINGER_MODE_NORMAL)
+
+            setUpRingerModeAndOpenDrawer(normalRingerMode)
+            underTest.onRingerButtonClicked(normalRingerMode)
+            controller.getState()
+
+            assertThat(ringerViewModel).isInstanceOf(RingerViewModelState.Available::class.java)
+            assertThat((ringerViewModel as RingerViewModelState.Available).uiModel.drawerState)
+                .isEqualTo(RingerDrawerState.Closed(normalRingerMode))
+        }
+
+    @Test
+    fun onNewRingerButtonClicked_drawerOpened_updateRingerMode_closeDrawer() =
+        testScope.runTest {
+            val ringerViewModel by collectLastValue(underTest.ringerViewModel)
+            val vibrateRingerMode = RingerMode(RINGER_MODE_VIBRATE)
+
+            setUpRingerModeAndOpenDrawer(RingerMode(RINGER_MODE_NORMAL))
+            // Select vibrate ringer mode.
+            underTest.onRingerButtonClicked(vibrateRingerMode)
+            controller.getState()
+            runCurrent()
+
+            assertThat(ringerViewModel).isInstanceOf(RingerViewModelState.Available::class.java)
+
+            var uiModel = (ringerViewModel as RingerViewModelState.Available).uiModel
+            assertThat(uiModel.availableButtons[uiModel.currentButtonIndex]?.ringerMode)
+                .isEqualTo(vibrateRingerMode)
+            assertThat(uiModel.drawerState).isEqualTo(RingerDrawerState.Closed(vibrateRingerMode))
+
+            val silentRingerMode = RingerMode(RINGER_MODE_SILENT)
+            // Open drawer
+            underTest.onRingerButtonClicked(vibrateRingerMode)
+            controller.getState()
+
+            // Select silent ringer mode.
+            underTest.onRingerButtonClicked(silentRingerMode)
+            controller.getState()
+            runCurrent()
+
+            assertThat(ringerViewModel).isInstanceOf(RingerViewModelState.Available::class.java)
+
+            uiModel = (ringerViewModel as RingerViewModelState.Available).uiModel
+            assertThat(uiModel.availableButtons[uiModel.currentButtonIndex]?.ringerMode)
+                .isEqualTo(silentRingerMode)
+            assertThat(uiModel.drawerState).isEqualTo(RingerDrawerState.Closed(silentRingerMode))
+            assertThat(controller.hasScheduledTouchFeedback).isFalse()
+            assertThat(vibratorHelper.totalVibrations).isEqualTo(2)
+        }
+
+    @Test
+    fun onVolumeSingleMode_ringerIsUnavailable() =
+        testScope.runTest {
+            val ringerViewModel by collectLastValue(underTest.ringerViewModel)
+
+            kosmos.audioSystemRepository.setIsSingleVolume(true)
+            setUpRingerMode(RingerMode(RINGER_MODE_NORMAL))
+
+            assertThat(ringerViewModel).isInstanceOf(RingerViewModelState.Unavailable::class.java)
+        }
+
+    @Test
+    fun setUnsupportedRingerMode_ringerIsUnavailable() =
+        testScope.runTest {
+            val ringerViewModel by collectLastValue(underTest.ringerViewModel)
+
+            controller.setHasVibrator(false)
+            setUpRingerMode(RingerMode(RINGER_MODE_VIBRATE))
+
+            assertThat(ringerViewModel).isInstanceOf(RingerViewModelState.Unavailable::class.java)
+        }
+
+    private fun TestScope.setUpRingerModeAndOpenDrawer(selectedRingerMode: RingerMode) {
+        setUpRingerMode(selectedRingerMode)
+        underTest.onRingerButtonClicked(RingerMode(selectedRingerMode.value))
+        controller.getState()
+        runCurrent()
+    }
+
+    private fun TestScope.setUpRingerMode(selectedRingerMode: RingerMode) {
+        controller.setStreamVolume(STREAM_RING, 50)
+        controller.setRingerMode(selectedRingerMode.value, false)
+        runCurrent()
+    }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSliderInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSliderInteractorTest.kt
new file mode 100644
index 0000000..bfafdab
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSliderInteractorTest.kt
@@ -0,0 +1,90 @@
+/*
+ * 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.dialog.sliders.domain.interactor
+
+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.testScope
+import com.android.systemui.plugins.VolumeDialogController
+import com.android.systemui.plugins.fakeVolumeDialogController
+import com.android.systemui.testKosmos
+import com.android.systemui.volume.dialog.sliders.domain.model.volumeDialogSliderType
+import com.google.common.truth.Truth.assertThat
+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
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+@TestableLooper.RunWithLooper
+class VolumeDialogSliderInteractorTest : SysuiTestCase() {
+
+    private val kosmos = testKosmos()
+
+    private lateinit var underTest: VolumeDialogSliderInteractor
+
+    @Before
+    fun setUp() {
+        underTest = kosmos.volumeDialogSliderInteractor
+    }
+
+    @Test
+    fun settingStreamVolume_setsActiveStream() =
+        with(kosmos) {
+            testScope.runTest {
+                runCurrent()
+                // initialize the stream model
+                fakeVolumeDialogController.setStreamVolume(volumeDialogSliderType.audioStream, 0)
+
+                val sliderModel by collectLastValue(underTest.slider)
+                underTest.setStreamVolume(1)
+                runCurrent()
+
+                assertThat(sliderModel!!.isActive).isTrue()
+            }
+        }
+
+    @Test
+    fun streamVolumeIs_minMaxAreEnforced() =
+        with(kosmos) {
+            testScope.runTest {
+                runCurrent()
+                fakeVolumeDialogController.updateState {
+                    states.put(
+                        volumeDialogSliderType.audioStream,
+                        VolumeDialogController.StreamState().apply {
+                            levelMin = 0
+                            level = 2
+                            levelMax = 1
+                        },
+                    )
+                }
+
+                val sliderModel by collectLastValue(underTest.slider)
+                runCurrent()
+
+                assertThat(sliderModel!!.level).isEqualTo(1)
+            }
+        }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModelTest.kt
index f80b36a..d3071f8 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModelTest.kt
@@ -28,6 +28,7 @@
 import com.android.settingslib.volume.shared.model.AudioStream
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.haptics.slider.sliderHapticsViewModelFactory
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.statusbar.policy.data.repository.fakeZenModeRepository
 import com.android.systemui.statusbar.policy.domain.interactor.zenModeInteractor
@@ -73,6 +74,7 @@
             kosmos.zenModeInteractor,
             kosmos.uiEventLogger,
             kosmos.volumePanelLogger,
+            kosmos.sliderHapticsViewModelFactory,
         )
     }
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/wallpapers/data/repository/FakeWallpaperRepository.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/wallpapers/data/repository/FakeWallpaperRepository.kt
index 59676ce..111c232 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/wallpapers/data/repository/FakeWallpaperRepository.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/wallpapers/data/repository/FakeWallpaperRepository.kt
@@ -25,9 +25,4 @@
     override val wallpaperInfo = MutableStateFlow<WallpaperInfo?>(null)
     override val wallpaperSupportsAmbientMode = MutableStateFlow(false)
     override var rootView: View? = null
-    private val _notificationStackAbsoluteBottom = MutableStateFlow(0F)
-
-    override fun setNotificationStackAbsoluteBottom(bottom: Float) {
-        _notificationStackAbsoluteBottom.value = bottom
-    }
 }
diff --git a/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceConfigPlugin.kt b/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceConfigPlugin.kt
index 84f39af..d16017a 100644
--- a/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceConfigPlugin.kt
+++ b/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceConfigPlugin.kt
@@ -23,4 +23,6 @@
     val isDefaultDateWeatherDisabled: Boolean
     /** Gets if Smartspace should use ViewPager2 */
     val isViewPager2Enabled: Boolean
+    /** Gets if card swipe event should be logged */
+    val isSwipeEventLoggingEnabled: Boolean
 }
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 f975c4f..e264264 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
@@ -182,7 +182,7 @@
 interface ClockEvents {
     @get:ProtectedReturn("return false;")
     /** Set to enable or disable swipe interaction */
-    var isReactiveTouchInteractionEnabled: Boolean
+    var isReactiveTouchInteractionEnabled: Boolean // TODO(b/364664388): Remove/Rename
 
     /** Call whenever timezone changes */
     fun onTimeZoneChanged(timeZone: TimeZone)
@@ -322,9 +322,6 @@
     /** True if the clock will react to tone changes in the seed color */
     val isReactiveToTone: Boolean = true,
 
-    /** True if the clock is capable of changing style in reaction to touches */
-    val isReactiveToTouch: Boolean = false,
-
     /** Font axes that can be modified on this clock */
     val axes: List<ClockReactiveAxis> = listOf(),
 )
diff --git a/packages/SystemUI/plugin_core/AndroidManifest.xml b/packages/SystemUI/plugin_core/AndroidManifest.xml
deleted file mode 100644
index df835fd..0000000
--- a/packages/SystemUI/plugin_core/AndroidManifest.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-     Copyright (C) 2018 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.
--->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.android.systemui.plugin_core">
-
-    <uses-sdk
-        android:minSdkVersion="28" />
-
-</manifest>
diff --git a/packages/SystemUI/res/drawable/notif_footer_btn_history.xml b/packages/SystemUI/res/drawable/notif_footer_btn_history.xml
new file mode 100644
index 0000000..0460a72
--- /dev/null
+++ b/packages/SystemUI/res/drawable/notif_footer_btn_history.xml
@@ -0,0 +1,10 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="20dp"
+    android:height="20dp"
+    android:viewportWidth="960"
+    android:viewportHeight="960"
+    android:tint="?attr/colorControlNormal">
+  <path
+      android:fillColor="@android:color/white"
+      android:pathData="M480,840Q342,840 239.5,748.5Q137,657 122,520L204,520Q218,624 296.5,692Q375,760 480,760Q597,760 678.5,678.5Q760,597 760,480Q760,363 678.5,281.5Q597,200 480,200Q411,200 351,232Q291,264 250,320L360,320L360,400L120,400L120,160L200,160L200,254Q251,190 324.5,155Q398,120 480,120Q555,120 620.5,148.5Q686,177 734.5,225.5Q783,274 811.5,339.5Q840,405 840,480Q840,555 811.5,620.5Q783,686 734.5,734.5Q686,783 620.5,811.5Q555,840 480,840ZM592,648L440,496L440,280L520,280L520,464L648,592L592,648Z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/notif_footer_btn_settings.xml b/packages/SystemUI/res/drawable/notif_footer_btn_settings.xml
new file mode 100644
index 0000000..800060d
--- /dev/null
+++ b/packages/SystemUI/res/drawable/notif_footer_btn_settings.xml
@@ -0,0 +1,10 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="20dp"
+    android:height="20dp"
+    android:viewportWidth="960"
+    android:viewportHeight="960"
+    android:tint="?attr/colorControlNormal">
+  <path
+      android:fillColor="@android:color/white"
+      android:pathData="M370,880L354,752Q341,747 329.5,740Q318,733 307,725L188,775L78,585L181,507Q180,500 180,493.5Q180,487 180,480Q180,473 180,466.5Q180,460 181,453L78,375L188,185L307,235Q318,227 330,220Q342,213 354,208L370,80L590,80L606,208Q619,213 630.5,220Q642,227 653,235L772,185L882,375L779,453Q780,460 780,466.5Q780,473 780,480Q780,487 780,493.5Q780,500 778,507L881,585L771,775L653,725Q642,733 630,740Q618,747 606,752L590,880L370,880ZM440,800L519,800L533,694Q564,686 590.5,670.5Q617,655 639,633L738,674L777,606L691,541Q696,527 698,511.5Q700,496 700,480Q700,464 698,448.5Q696,433 691,419L777,354L738,286L639,328Q617,305 590.5,289.5Q564,274 533,266L520,160L441,160L427,266Q396,274 369.5,289.5Q343,305 321,327L222,286L183,354L269,418Q264,433 262,448Q260,463 260,480Q260,496 262,511Q264,526 269,541L183,606L222,674L321,632Q343,655 369.5,670.5Q396,686 427,694L440,800ZM482,620Q540,620 581,579Q622,538 622,480Q622,422 581,381Q540,340 482,340Q423,340 382.5,381Q342,422 342,480Q342,538 382.5,579Q423,620 482,620ZM480,480L480,480Q480,480 480,480Q480,480 480,480L480,480L480,480L480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480L480,480L480,480L480,480Q480,480 480,480Q480,480 480,480L480,480L480,480L480,480Q480,480 480,480Q480,480 480,480L480,480L480,480L480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480L480,480L480,480L480,480Q480,480 480,480Q480,480 480,480L480,480Z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/volume_background_top.xml b/packages/SystemUI/res/drawable/volume_background_top.xml
index 3cd87fc..75849be 100644
--- a/packages/SystemUI/res/drawable/volume_background_top.xml
+++ b/packages/SystemUI/res/drawable/volume_background_top.xml
@@ -1,27 +1,26 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2021 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
-  -->
+<?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"
     xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
     <item>
         <shape>
-            <size android:width="@dimen/volume_dialog_panel_width" />
-            <solid android:color="?androidprv:attr/colorSurface" />
-            <corners android:topLeftRadius="@dimen/volume_dialog_panel_width_half"
-                android:topRightRadius="@dimen/volume_dialog_panel_width_half"/>
+            <size android:width="@dimen/volume_dialog_width" />
+            <solid android:color="?androidprv:attr/materialColorSurface" />
+            <corners android:topLeftRadius="@dimen/volume_dialog_background_corner_radius"
+                android:topRightRadius="@dimen/volume_dialog_background_corner_radius"/>
         </shape>
     </item>
-</layer-list>
\ No newline at end of file
+</layer-list>
diff --git a/packages/SystemUI/res/drawable/volume_background_top_legacy.xml b/packages/SystemUI/res/drawable/volume_background_top_legacy.xml
new file mode 100644
index 0000000..3cd87fc
--- /dev/null
+++ b/packages/SystemUI/res/drawable/volume_background_top_legacy.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2021 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"
+    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
+    <item>
+        <shape>
+            <size android:width="@dimen/volume_dialog_panel_width" />
+            <solid android:color="?androidprv:attr/colorSurface" />
+            <corners android:topLeftRadius="@dimen/volume_dialog_panel_width_half"
+                android:topRightRadius="@dimen/volume_dialog_panel_width_half"/>
+        </shape>
+    </item>
+</layer-list>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/volume_drawer_selection_bg.xml b/packages/SystemUI/res/drawable/volume_drawer_selection_bg.xml
index 5e7cb12..df8521a 100644
--- a/packages/SystemUI/res/drawable/volume_drawer_selection_bg.xml
+++ b/packages/SystemUI/res/drawable/volume_drawer_selection_bg.xml
@@ -1,6 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ 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,10 +14,11 @@
   ~ limitations under the License.
   -->
 <shape xmlns:android="http://schemas.android.com/apk/res/android"
-            android:paddingMode="stack" >
+    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+    android:paddingMode="stack" >
     <size
-        android:height="@dimen/volume_ringer_drawer_item_size"
-        android:width="@dimen/volume_ringer_drawer_item_size" />
-    <solid android:color="?android:attr/colorAccent" />
-    <corners android:radius="@dimen/volume_ringer_drawer_item_size_half" />
+        android:height="@dimen/volume_ringer_item_size"
+        android:width="@dimen/volume_ringer_item_size" />
+    <solid android:color="?androidprv:attr/materialColorPrimary" />
+    <corners android:radius="360dp" />
 </shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/volume_drawer_selection_bg_legacy.xml b/packages/SystemUI/res/drawable/volume_drawer_selection_bg_legacy.xml
new file mode 100644
index 0000000..5e7cb12
--- /dev/null
+++ b/packages/SystemUI/res/drawable/volume_drawer_selection_bg_legacy.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2021 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.
+  -->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+            android:paddingMode="stack" >
+    <size
+        android:height="@dimen/volume_ringer_drawer_item_size"
+        android:width="@dimen/volume_ringer_drawer_item_size" />
+    <solid android:color="?android:attr/colorAccent" />
+    <corners android:radius="@dimen/volume_ringer_drawer_item_size_half" />
+</shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/volume_ringer_item_bg.xml b/packages/SystemUI/res/drawable/volume_ringer_item_bg.xml
new file mode 100644
index 0000000..7ddd57b7
--- /dev/null
+++ b/packages/SystemUI/res/drawable/volume_ringer_item_bg.xml
@@ -0,0 +1,22 @@
+<?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.
+  -->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+    android:shape="rectangle" >
+    <size android:width="@dimen/volume_ringer_item_size" android:height="@dimen/volume_ringer_item_size"/>
+    <solid android:color="?androidprv:attr/materialColorSurfaceContainerHighest" />
+    <corners android:radius="@dimen/volume_ringer_item_radius" />
+</shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout-land-television/volume_dialog.xml b/packages/SystemUI/res/layout-land-television/volume_dialog.xml
deleted file mode 100644
index f77db95..0000000
--- a/packages/SystemUI/res/layout-land-television/volume_dialog.xml
+++ /dev/null
@@ -1,67 +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.
--->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
-    android:id="@+id/volume_dialog_container"
-    android:layout_width="wrap_content"
-    android:layout_height="wrap_content"
-    android:layout_gravity="right"
-    android:divider="@drawable/volume_dialog_floating_sliders_spacer"
-    android:orientation="horizontal"
-    android:showDividers="middle|end|beginning"
-    android:theme="@style/volume_dialog_theme">
-
-    <LinearLayout
-        android:id="@+id/volume_dialog_floating_sliders_container"
-        android:layout_width="wrap_content"
-        android:layout_height="match_parent"
-        android:background="@drawable/volume_dialog_background"
-        android:divider="@drawable/volume_dialog_floating_sliders_spacer"
-        android:gravity="bottom"
-        android:orientation="horizontal"
-        android:paddingBottom="@dimen/volume_dialog_floating_sliders_bottom_padding"
-        android:showDividers="middle" />
-
-    <LinearLayout
-        android:layout_width="@dimen/volume_dialog_width"
-        android:layout_height="wrap_content"
-        android:background="@drawable/volume_dialog_background"
-        android:divider="@drawable/volume_dialog_spacer"
-        android:gravity="center_horizontal"
-        android:orientation="vertical"
-        android:paddingVertical="@dimen/volume_dialog_vertical_padding"
-        android:showDividers="middle">
-
-        <FrameLayout
-            android:id="@+id/volume_dialog_ringer_button"
-            android:layout_width="@dimen/volume_dialog_button_size"
-            android:layout_height="@dimen/volume_dialog_button_size" />
-
-        <include
-            android:id="@+id/volume_dialog_slider"
-            layout="@layout/volume_dialog_slider" />
-
-        <Button
-            android:id="@+id/volume_dialog_settings"
-            android:layout_width="@dimen/volume_dialog_button_size"
-            android:layout_height="@dimen/volume_dialog_button_size"
-            android:background="@drawable/ripple_drawable_20dp"
-            android:contentDescription="@string/accessibility_volume_settings"
-            android:soundEffectsEnabled="false"
-            android:src="@drawable/horizontal_ellipsis"
-            android:tint="?androidprv:attr/materialColorPrimary" />
-    </LinearLayout>
-</LinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout-land/volume_dialog.xml b/packages/SystemUI/res/layout-land/volume_dialog.xml
deleted file mode 100644
index f77db95..0000000
--- a/packages/SystemUI/res/layout-land/volume_dialog.xml
+++ /dev/null
@@ -1,67 +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.
--->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
-    android:id="@+id/volume_dialog_container"
-    android:layout_width="wrap_content"
-    android:layout_height="wrap_content"
-    android:layout_gravity="right"
-    android:divider="@drawable/volume_dialog_floating_sliders_spacer"
-    android:orientation="horizontal"
-    android:showDividers="middle|end|beginning"
-    android:theme="@style/volume_dialog_theme">
-
-    <LinearLayout
-        android:id="@+id/volume_dialog_floating_sliders_container"
-        android:layout_width="wrap_content"
-        android:layout_height="match_parent"
-        android:background="@drawable/volume_dialog_background"
-        android:divider="@drawable/volume_dialog_floating_sliders_spacer"
-        android:gravity="bottom"
-        android:orientation="horizontal"
-        android:paddingBottom="@dimen/volume_dialog_floating_sliders_bottom_padding"
-        android:showDividers="middle" />
-
-    <LinearLayout
-        android:layout_width="@dimen/volume_dialog_width"
-        android:layout_height="wrap_content"
-        android:background="@drawable/volume_dialog_background"
-        android:divider="@drawable/volume_dialog_spacer"
-        android:gravity="center_horizontal"
-        android:orientation="vertical"
-        android:paddingVertical="@dimen/volume_dialog_vertical_padding"
-        android:showDividers="middle">
-
-        <FrameLayout
-            android:id="@+id/volume_dialog_ringer_button"
-            android:layout_width="@dimen/volume_dialog_button_size"
-            android:layout_height="@dimen/volume_dialog_button_size" />
-
-        <include
-            android:id="@+id/volume_dialog_slider"
-            layout="@layout/volume_dialog_slider" />
-
-        <Button
-            android:id="@+id/volume_dialog_settings"
-            android:layout_width="@dimen/volume_dialog_button_size"
-            android:layout_height="@dimen/volume_dialog_button_size"
-            android:background="@drawable/ripple_drawable_20dp"
-            android:contentDescription="@string/accessibility_volume_settings"
-            android:soundEffectsEnabled="false"
-            android:src="@drawable/horizontal_ellipsis"
-            android:tint="?androidprv:attr/materialColorPrimary" />
-    </LinearLayout>
-</LinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout-land/volume_dialog_legacy.xml b/packages/SystemUI/res/layout-land/volume_dialog_legacy.xml
index 08edf59..de346db 100644
--- a/packages/SystemUI/res/layout-land/volume_dialog_legacy.xml
+++ b/packages/SystemUI/res/layout-land/volume_dialog_legacy.xml
@@ -44,7 +44,7 @@
             android:clipChildren="false"
             android:gravity="right">
 
-            <include layout="@layout/volume_ringer_drawer" />
+            <include layout="@layout/volume_ringer_drawer_legacy" />
 
             <FrameLayout
                 android:visibility="gone"
diff --git a/packages/SystemUI/res/layout/status_bar_notification_footer_redesign.xml b/packages/SystemUI/res/layout/status_bar_notification_footer_redesign.xml
new file mode 100644
index 0000000..7c59aad
--- /dev/null
+++ b/packages/SystemUI/res/layout/status_bar_notification_footer_redesign.xml
@@ -0,0 +1,82 @@
+<!--
+  ~ 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
+  -->
+
+<!-- Extends Framelayout -->
+<com.android.systemui.statusbar.notification.footer.ui.view.FooterView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:visibility="gone">
+
+    <com.android.systemui.statusbar.AlphaOptimizedFrameLayout
+        android:id="@+id/content"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:paddingTop="12dp">
+
+        <TextView
+            android:id="@+id/unlock_prompt_footer"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center_horizontal"
+            android:drawablePadding="8dp"
+            android:gravity="center"
+            android:text="@string/unlock_to_see_notif_text"
+            android:textAppearance="?android:attr/textAppearanceButton"
+            android:visibility="gone" />
+
+        <androidx.constraintlayout.widget.ConstraintLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content">
+
+            <com.android.systemui.statusbar.notification.row.FooterViewButton
+                android:id="@+id/settings_button"
+                style="@style/TextAppearance.NotificationFooterButtonRedesign"
+                android:layout_width="wrap_content"
+                android:layout_height="48dp"
+                android:background="@drawable/notif_footer_btn_background"
+                android:contentDescription="@string/notification_settings_button_description"
+                android:drawableStart="@drawable/notif_footer_btn_settings"
+                android:focusable="true"
+                app:layout_constraintStart_toStartOf="parent" />
+
+            <com.android.systemui.statusbar.notification.row.FooterViewButton
+                android:id="@+id/dismiss_text"
+                style="@style/TextAppearance.NotificationFooterButtonRedesign"
+                android:layout_width="0dp"
+                android:layout_height="48dp"
+                android:layout_marginHorizontal="8dp"
+                android:background="@drawable/notif_footer_btn_background"
+                android:contentDescription="@string/accessibility_clear_all"
+                android:focusable="true"
+                android:text="@string/clear_all_notifications_text"
+                app:layout_constraintEnd_toStartOf="@id/history_button"
+                app:layout_constraintStart_toEndOf="@id/settings_button" />
+
+            <com.android.systemui.statusbar.notification.row.FooterViewButton
+                android:id="@+id/history_button"
+                style="@style/TextAppearance.NotificationFooterButtonRedesign"
+                android:layout_width="wrap_content"
+                android:layout_height="48dp"
+                android:background="@drawable/notif_footer_btn_background"
+                android:contentDescription="@string/notification_history_button_description"
+                android:drawableStart="@drawable/notif_footer_btn_history"
+                android:focusable="true"
+                app:layout_constraintEnd_toEndOf="parent" />
+        </androidx.constraintlayout.widget.ConstraintLayout>
+    </com.android.systemui.statusbar.AlphaOptimizedFrameLayout>
+</com.android.systemui.statusbar.notification.footer.ui.view.FooterView>
diff --git a/packages/SystemUI/res/layout/volume_dialog.xml b/packages/SystemUI/res/layout/volume_dialog.xml
index f77db95..8b39e5e 100644
--- a/packages/SystemUI/res/layout/volume_dialog.xml
+++ b/packages/SystemUI/res/layout/volume_dialog.xml
@@ -28,7 +28,6 @@
         android:id="@+id/volume_dialog_floating_sliders_container"
         android:layout_width="wrap_content"
         android:layout_height="match_parent"
-        android:background="@drawable/volume_dialog_background"
         android:divider="@drawable/volume_dialog_floating_sliders_spacer"
         android:gravity="bottom"
         android:orientation="horizontal"
@@ -36,6 +35,7 @@
         android:showDividers="middle" />
 
     <LinearLayout
+        android:id="@+id/volume_dialog"
         android:layout_width="@dimen/volume_dialog_width"
         android:layout_height="wrap_content"
         android:background="@drawable/volume_dialog_background"
@@ -45,16 +45,11 @@
         android:paddingVertical="@dimen/volume_dialog_vertical_padding"
         android:showDividers="middle">
 
-        <FrameLayout
-            android:id="@+id/volume_dialog_ringer_button"
-            android:layout_width="@dimen/volume_dialog_button_size"
-            android:layout_height="@dimen/volume_dialog_button_size" />
+        <include layout="@layout/volume_ringer_drawer" />
 
-        <include
-            android:id="@+id/volume_dialog_slider"
-            layout="@layout/volume_dialog_slider" />
+        <include layout="@layout/volume_dialog_slider" />
 
-        <Button
+        <ImageButton
             android:id="@+id/volume_dialog_settings"
             android:layout_width="@dimen/volume_dialog_button_size"
             android:layout_height="@dimen/volume_dialog_button_size"
diff --git a/packages/SystemUI/res/layout/volume_dialog_legacy.xml b/packages/SystemUI/res/layout/volume_dialog_legacy.xml
index 39a1f1f..9010ab7 100644
--- a/packages/SystemUI/res/layout/volume_dialog_legacy.xml
+++ b/packages/SystemUI/res/layout/volume_dialog_legacy.xml
@@ -45,7 +45,7 @@
             android:orientation="vertical"
             android:gravity="right">
 
-            <include layout="@layout/volume_ringer_drawer" />
+            <include layout="@layout/volume_ringer_drawer_legacy" />
 
             <FrameLayout
                 android:visibility="gone"
diff --git a/packages/SystemUI/res/layout/volume_ringer_drawer.xml b/packages/SystemUI/res/layout/volume_ringer_drawer.xml
index 8f1e061..94b55b1 100644
--- a/packages/SystemUI/res/layout/volume_ringer_drawer.xml
+++ b/packages/SystemUI/res/layout/volume_ringer_drawer.xml
@@ -1,35 +1,32 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-     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.
+  ~ 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.
+  -->
 
-     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.
--->
-
-<!-- Contains the active ringer icon and a hidden drawer containing all three ringer options. -->
-<FrameLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
     android:id="@+id/volume_ringer_and_drawer_container"
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
     android:gravity="center"
-    android:paddingLeft="@dimen/volume_dialog_ringer_rows_padding"
-    android:paddingTop="@dimen/volume_dialog_ringer_rows_padding"
-    android:paddingRight="@dimen/volume_dialog_ringer_rows_padding"
-    android:paddingBottom="@dimen/volume_dialog_ringer_rows_padding"
-    android:background="@drawable/volume_background_top"
+    android:paddingLeft="@dimen/volume_dialog_ringer_container_padding"
+    android:paddingTop="@dimen/volume_dialog_ringer_container_padding"
+    android:paddingRight="@dimen/volume_dialog_ringer_container_padding"
     android:layoutDirection="ltr"
     android:clipToPadding="false"
-    android:clipChildren="false">
+    android:clipChildren="false"
+    android:background="@drawable/volume_background_top">
 
     <!-- Drawer view, invisible by default. -->
     <FrameLayout
@@ -37,15 +34,15 @@
         android:alpha="0.0"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:background="@drawable/volume_drawer_bg"
         android:orientation="vertical">
 
         <!-- View that is animated to a tapped ringer selection, so it appears selected. -->
         <FrameLayout
             android:id="@+id/volume_drawer_selection_background"
             android:alpha="0.0"
-            android:layout_width="@dimen/volume_ringer_drawer_item_size"
-            android:layout_height="@dimen/volume_ringer_drawer_item_size"
+            android:layout_width="@dimen/volume_ringer_item_size"
+            android:layout_marginBottom="8dp"
+            android:layout_height="@dimen/volume_ringer_item_size"
             android:layout_gravity="bottom|right"
             android:background="@drawable/volume_drawer_selection_bg" />
 
@@ -57,54 +54,60 @@
 
             <FrameLayout
                 android:id="@+id/volume_drawer_vibrate"
-                android:layout_width="@dimen/volume_ringer_drawer_item_size"
-                android:layout_height="@dimen/volume_ringer_drawer_item_size"
+                android:layout_width="@dimen/volume_ringer_item_size"
+                android:layout_height="@dimen/volume_ringer_item_size"
+                android:layout_marginBottom="8dp"
                 android:contentDescription="@string/volume_ringer_hint_vibrate"
-                android:gravity="center">
+                android:gravity="center"
+                android:background="@drawable/volume_ringer_item_bg">
 
                 <ImageView
                     android:id="@+id/volume_drawer_vibrate_icon"
-                    android:layout_width="@dimen/volume_ringer_drawer_icon_size"
-                    android:layout_height="@dimen/volume_ringer_drawer_icon_size"
+                    android:layout_width="@dimen/volume_ringer_icon_size"
+                    android:layout_height="@dimen/volume_ringer_icon_size"
                     android:layout_gravity="center"
                     android:src="@drawable/ic_volume_ringer_vibrate"
-                    android:tint="?android:attr/textColorPrimary" />
+                    android:tint="?androidprv:attr/materialColorOnSurface" />
 
             </FrameLayout>
 
             <FrameLayout
                 android:id="@+id/volume_drawer_mute"
-                android:layout_width="@dimen/volume_ringer_drawer_item_size"
-                android:layout_height="@dimen/volume_ringer_drawer_item_size"
+                android:layout_width="@dimen/volume_ringer_item_size"
+                android:layout_height="@dimen/volume_ringer_item_size"
+                android:layout_marginBottom="8dp"
                 android:accessibilityTraversalAfter="@id/volume_drawer_vibrate"
                 android:contentDescription="@string/volume_ringer_hint_mute"
-                android:gravity="center">
+                android:gravity="center"
+                android:background="@drawable/volume_ringer_item_bg">
 
                 <ImageView
                     android:id="@+id/volume_drawer_mute_icon"
-                    android:layout_width="@dimen/volume_ringer_drawer_icon_size"
-                    android:layout_height="@dimen/volume_ringer_drawer_icon_size"
+                    android:layout_width="@dimen/volume_ringer_icon_size"
+                    android:layout_height="@dimen/volume_ringer_icon_size"
                     android:layout_gravity="center"
                     android:src="@drawable/ic_speaker_mute"
-                    android:tint="?android:attr/textColorPrimary" />
+                    android:tint="?androidprv:attr/materialColorOnSurface" />
 
             </FrameLayout>
 
             <FrameLayout
                 android:id="@+id/volume_drawer_normal"
-                android:layout_width="@dimen/volume_ringer_drawer_item_size"
-                android:layout_height="@dimen/volume_ringer_drawer_item_size"
+                android:layout_width="@dimen/volume_ringer_item_size"
+                android:layout_height="@dimen/volume_ringer_item_size"
+                android:layout_marginBottom="8dp"
                 android:accessibilityTraversalAfter="@id/volume_drawer_mute"
                 android:contentDescription="@string/volume_ringer_hint_unmute"
-                android:gravity="center">
+                android:gravity="center"
+                android:background="@drawable/volume_ringer_item_bg">
 
                 <ImageView
                     android:id="@+id/volume_drawer_normal_icon"
-                    android:layout_width="@dimen/volume_ringer_drawer_icon_size"
-                    android:layout_height="@dimen/volume_ringer_drawer_icon_size"
+                    android:layout_width="@dimen/volume_ringer_icon_size"
+                    android:layout_height="@dimen/volume_ringer_icon_size"
                     android:layout_gravity="center"
                     android:src="@drawable/ic_speaker_on"
-                    android:tint="?android:attr/textColorPrimary" />
+                    android:tint="?androidprv:attr/materialColorOnSurface" />
 
             </FrameLayout>
 
@@ -116,18 +119,19 @@
          position in the drawer. When the drawer is closed, it animates back. -->
     <FrameLayout
         android:id="@+id/volume_new_ringer_active_icon_container"
-        android:layout_width="@dimen/volume_ringer_drawer_item_size"
-        android:layout_height="@dimen/volume_ringer_drawer_item_size"
+        android:layout_width="@dimen/volume_ringer_item_size"
+        android:layout_height="@dimen/volume_ringer_item_size"
+        android:layout_marginBottom="8dp"
         android:layout_gravity="bottom|right"
         android:contentDescription="@string/volume_ringer_change"
         android:background="@drawable/volume_drawer_selection_bg">
 
         <ImageView
             android:id="@+id/volume_new_ringer_active_icon"
-            android:layout_width="@dimen/volume_ringer_drawer_icon_size"
-            android:layout_height="@dimen/volume_ringer_drawer_icon_size"
+            android:layout_width="@dimen/volume_ringer_icon_size"
+            android:layout_height="@dimen/volume_ringer_icon_size"
             android:layout_gravity="center"
-            android:tint="?android:attr/textColorPrimaryInverse"
+            android:tint="?androidprv:attr/materialColorOnPrimary"
             android:src="@drawable/ic_volume_media" />
 
     </FrameLayout>
diff --git a/packages/SystemUI/res/layout/volume_ringer_drawer_legacy.xml b/packages/SystemUI/res/layout/volume_ringer_drawer_legacy.xml
new file mode 100644
index 0000000..0efbc6d
--- /dev/null
+++ b/packages/SystemUI/res/layout/volume_ringer_drawer_legacy.xml
@@ -0,0 +1,135 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2021 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.
+-->
+
+<!-- Contains the active ringer icon and a hidden drawer containing all three ringer options. -->
+<FrameLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/volume_ringer_and_drawer_container"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:gravity="center"
+    android:paddingLeft="@dimen/volume_dialog_ringer_rows_padding"
+    android:paddingTop="@dimen/volume_dialog_ringer_rows_padding"
+    android:paddingRight="@dimen/volume_dialog_ringer_rows_padding"
+    android:paddingBottom="@dimen/volume_dialog_ringer_rows_padding"
+    android:background="@drawable/volume_background_top_legacy"
+    android:layoutDirection="ltr"
+    android:clipToPadding="false"
+    android:clipChildren="false">
+
+    <!-- Drawer view, invisible by default. -->
+    <FrameLayout
+        android:id="@+id/volume_drawer_container"
+        android:alpha="0.0"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:background="@drawable/volume_drawer_bg"
+        android:orientation="vertical">
+
+        <!-- View that is animated to a tapped ringer selection, so it appears selected. -->
+        <FrameLayout
+            android:id="@+id/volume_drawer_selection_background"
+            android:alpha="0.0"
+            android:layout_width="@dimen/volume_ringer_drawer_item_size"
+            android:layout_height="@dimen/volume_ringer_drawer_item_size"
+            android:layout_gravity="bottom|right"
+            android:background="@drawable/volume_drawer_selection_bg_legacy" />
+
+        <LinearLayout
+            android:id="@+id/volume_drawer_options"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:orientation="vertical">
+
+            <FrameLayout
+                android:id="@+id/volume_drawer_vibrate"
+                android:layout_width="@dimen/volume_ringer_drawer_item_size"
+                android:layout_height="@dimen/volume_ringer_drawer_item_size"
+                android:contentDescription="@string/volume_ringer_hint_vibrate"
+                android:gravity="center">
+
+                <ImageView
+                    android:id="@+id/volume_drawer_vibrate_icon"
+                    android:layout_width="@dimen/volume_ringer_drawer_icon_size"
+                    android:layout_height="@dimen/volume_ringer_drawer_icon_size"
+                    android:layout_gravity="center"
+                    android:src="@drawable/ic_volume_ringer_vibrate"
+                    android:tint="?android:attr/textColorPrimary" />
+
+            </FrameLayout>
+
+            <FrameLayout
+                android:id="@+id/volume_drawer_mute"
+                android:layout_width="@dimen/volume_ringer_drawer_item_size"
+                android:layout_height="@dimen/volume_ringer_drawer_item_size"
+                android:accessibilityTraversalAfter="@id/volume_drawer_vibrate"
+                android:contentDescription="@string/volume_ringer_hint_mute"
+                android:gravity="center">
+
+                <ImageView
+                    android:id="@+id/volume_drawer_mute_icon"
+                    android:layout_width="@dimen/volume_ringer_drawer_icon_size"
+                    android:layout_height="@dimen/volume_ringer_drawer_icon_size"
+                    android:layout_gravity="center"
+                    android:src="@drawable/ic_speaker_mute"
+                    android:tint="?android:attr/textColorPrimary" />
+
+            </FrameLayout>
+
+            <FrameLayout
+                android:id="@+id/volume_drawer_normal"
+                android:layout_width="@dimen/volume_ringer_drawer_item_size"
+                android:layout_height="@dimen/volume_ringer_drawer_item_size"
+                android:accessibilityTraversalAfter="@id/volume_drawer_mute"
+                android:contentDescription="@string/volume_ringer_hint_unmute"
+                android:gravity="center">
+
+                <ImageView
+                    android:id="@+id/volume_drawer_normal_icon"
+                    android:layout_width="@dimen/volume_ringer_drawer_icon_size"
+                    android:layout_height="@dimen/volume_ringer_drawer_icon_size"
+                    android:layout_gravity="center"
+                    android:src="@drawable/ic_speaker_on"
+                    android:tint="?android:attr/textColorPrimary" />
+
+            </FrameLayout>
+
+        </LinearLayout>
+
+    </FrameLayout>
+
+    <!-- The current ringer selection. When the drawer is opened, this animates to the corresponding
+         position in the drawer. When the drawer is closed, it animates back. -->
+    <FrameLayout
+        android:id="@+id/volume_new_ringer_active_icon_container"
+        android:layout_width="@dimen/volume_ringer_drawer_item_size"
+        android:layout_height="@dimen/volume_ringer_drawer_item_size"
+        android:layout_gravity="bottom|right"
+        android:contentDescription="@string/volume_ringer_change"
+        android:background="@drawable/volume_drawer_selection_bg_legacy">
+
+        <ImageView
+            android:id="@+id/volume_new_ringer_active_icon"
+            android:layout_width="@dimen/volume_ringer_drawer_icon_size"
+            android:layout_height="@dimen/volume_ringer_drawer_icon_size"
+            android:layout_gravity="center"
+            android:tint="?android:attr/textColorPrimaryInverse"
+            android:src="@drawable/ic_volume_media" />
+
+    </FrameLayout>
+
+</FrameLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml
index 3f68258..9ec6106 100644
--- a/packages/SystemUI/res/values-af/strings.xml
+++ b/packages/SystemUI/res/values-af/strings.xml
@@ -136,11 +136,17 @@
     <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Jy neem tans <xliff:g id="APP_NAME">%1$s</xliff:g> op"</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Stop opname"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Deel tans skerm"</string>
+    <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
+    <skip />
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Hou op om skerm te deel?"</string>
+    <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
+    <skip />
     <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Jy deel tans jou hele skerm met <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>"</string>
     <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Jy deel tans jou hele skerm met ’n app"</string>
     <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"Jy deel tans <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>"</string>
     <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"Jy deel tans ’n app"</string>
+    <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
+    <skip />
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Hou op deel"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Skerm word tans uitgesaai"</string>
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Hou op uitsaai?"</string>
@@ -515,6 +521,10 @@
     <string name="communal_widget_picker_title" msgid="1953369090475731663">"Sluitskermlegstukke"</string>
     <string name="communal_widget_picker_description" msgid="490515450110487871">"Enigiemand kan legstukke op jou sluitskerm sien, selfs al is jou tablet gesluit."</string>
     <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"ontkies legstuk"</string>
+    <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
+    <skip />
+    <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
+    <skip />
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Sluitskermlegstukke"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Om ’n app met ’n legstuk oop te maak, sal jy moet verifieer dat dit jy is. Hou ook in gedagte dat enigeen dit kan bekyk, selfs wanneer jou tablet gesluit is. Sommige legstukke is moontlik nie vir jou sluitskerm bedoel nie en dit kan onveilig wees om dit hier by te voeg."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Het dit"</string>
@@ -571,6 +581,10 @@
     <string name="clear_all_notifications_text" msgid="348312370303046130">"Vee alles uit"</string>
     <string name="manage_notifications_text" msgid="6885645344647733116">"Bestuur"</string>
     <string name="manage_notifications_history_text" msgid="57055985396576230">"Geskiedenis"</string>
+    <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
+    <skip />
+    <!-- no translation found for notification_history_button_description (1578657591405033383) -->
+    <skip />
     <string name="notification_section_header_incoming" msgid="850925217908095197">"Nuut"</string>
     <string name="notification_section_header_gentle" msgid="6804099527336337197">"Stil"</string>
     <string name="notification_section_header_alerting" msgid="5581175033680477651">"Kennisgewings"</string>
@@ -1405,8 +1419,7 @@
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Vou ikoon uit"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"of"</string>
     <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Sleephandvatsel"</string>
-    <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) -->
-    <skip />
+    <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Sleutelbordinstellings"</string>
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Navigeer met jou sleutelbord"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Leer kortpadsleutels"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Navigeer met jou raakpaneel"</string>
@@ -1464,8 +1477,6 @@
     <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Verskaf deur apps"</string>
     <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Vertoon"</string>
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Onbekend"</string>
-    <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) -->
-    <skip />
-    <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) -->
-    <skip />
+    <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"Stel teëls terug"</string>
+    <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"Stel teëls terug na hul oorspronklike volgorde en groottes?"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml
index c66d7a6..598e224 100644
--- a/packages/SystemUI/res/values-am/strings.xml
+++ b/packages/SystemUI/res/values-am/strings.xml
@@ -136,11 +136,17 @@
     <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"በአሁኑ ጊዜ <xliff:g id="APP_NAME">%1$s</xliff:g> በመቅዳት ላይ ነዎት"</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"መቅረጽ አቁም"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"ማያ ገፅን በማጋራት ላይ"</string>
+    <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
+    <skip />
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"ማያ ገፅን ማጋራት ይቁም?"</string>
+    <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
+    <skip />
     <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"በአሁኑ ጊዜ ሙሉ ማያ ገፅዎን ከ<xliff:g id="HOST_APP_NAME">%1$s</xliff:g> ጋር በማጋራት ላይ ነዎት"</string>
     <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"በአሁኑ ጊዜ መሉ ማያ ገፅዎን ከመተግበሪያ ጋር በማጋራት ላይ ነዎት"</string>
     <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"በአሁኑ ጊዜ <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> በማጋራት ላይ ነዎት"</string>
     <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"በአሁኑ ጊዜ መተግበሪያ በማጋራት ላይ ነዎት"</string>
+    <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
+    <skip />
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"ማጋራት አቁም"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"ማያ ገፅን cast በማድረግ ላይ"</string>
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"cast ማድረግ ይቁም?"</string>
@@ -515,6 +521,10 @@
     <string name="communal_widget_picker_title" msgid="1953369090475731663">"የማያ ገፅ ቁልፍ ምግብሮች"</string>
     <string name="communal_widget_picker_description" msgid="490515450110487871">"የእርስዎ ጡባዊ ቁልፍ ተቆልፎ ቢሆን እንኳን ማንኛውም ሰው በማያ ገፅ ቁልፍዎ ላይ ምግብሮችን ማየት ይችላል።"</string>
     <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"ምግብር አትምረጥ"</string>
+    <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
+    <skip />
+    <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
+    <skip />
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"የማያ ገፅ ቁልፍ ምግብሮች"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"ምግብር በመጠቀም መተግበሪያ ለመክፈት እርስዎ መሆንዎን ማረጋገጥ አለብዎት። እንዲሁም የእርስዎ ጡባዊ በተቆለፈበት ጊዜ እንኳን ማንኛውም ሰው እነሱን ማየት እንደሚችል ከግምት ውስጥ ያስገቡ። አንዳንድ ምግብሮች ለማያ ገፅ ቁልፍዎ የታሰቡ ላይሆኑ ይችላሉ እና እዚህ ለማከል አስተማማኝ ላይሆኑ ይችላሉ።"</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"ገባኝ"</string>
@@ -571,6 +581,10 @@
     <string name="clear_all_notifications_text" msgid="348312370303046130">"ሁሉንም አጽዳ"</string>
     <string name="manage_notifications_text" msgid="6885645344647733116">"ያቀናብሩ"</string>
     <string name="manage_notifications_history_text" msgid="57055985396576230">"ታሪክ"</string>
+    <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
+    <skip />
+    <!-- no translation found for notification_history_button_description (1578657591405033383) -->
+    <skip />
     <string name="notification_section_header_incoming" msgid="850925217908095197">"አዲስ"</string>
     <string name="notification_section_header_gentle" msgid="6804099527336337197">"ጸጥ ያለ"</string>
     <string name="notification_section_header_alerting" msgid="5581175033680477651">"ማሳወቂያዎች"</string>
@@ -1405,8 +1419,7 @@
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"መዘርጊያ አዶ"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ወይም"</string>
     <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"መያዣ ይጎትቱ"</string>
-    <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) -->
-    <skip />
+    <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"የቁልፍ ሰሌዳ ቅንብሮች"</string>
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"የቁልፍ ሰሌዳዎን በመጠቀም ያስሱ"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"የቁልፍ ሰሌዳ አቋራጮችን ይወቁ"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"የመዳሰሻ ሰሌዳዎን በመጠቀም ያስሱ"</string>
@@ -1464,8 +1477,6 @@
     <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"በመተግበሪያዎች የቀረበ"</string>
     <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"ማሳያ"</string>
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"ያልታወቀ"</string>
-    <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) -->
-    <skip />
-    <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) -->
-    <skip />
+    <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"ሰቆችን ዳግም ያስጀምሩ"</string>
+    <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"ሰቆችን ወደ የመጀመሪያው ቅደም ተከተል እና መጠኖቻቸው ይመለሱ?"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml
index c559488..342c8c2 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/strings.xml
@@ -136,11 +136,17 @@
     <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"يتم حاليًا تسجيل محتوى \"<xliff:g id="APP_NAME">%1$s</xliff:g>\""</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"إيقاف التسجيل"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"جارِ مشاركة محتوى الشاشة"</string>
+    <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
+    <skip />
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"هل تريد إيقاف مشاركة الشاشة؟"</string>
+    <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
+    <skip />
     <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"تتم حاليًا مشاركة محتوى الشاشة بأكمله مع \"<xliff:g id="HOST_APP_NAME">%1$s</xliff:g>\""</string>
     <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"تتم حاليًا مشاركة محتوى الشاشة بأكمله مع تطبيق"</string>
     <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"تتم حاليًا مشاركة محتوى \"<xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>\""</string>
     <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"تتم حاليًا مشاركة محتوى تطبيق"</string>
+    <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
+    <skip />
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"إيقاف المشاركة"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"جارٍ بث محتوى الشاشة"</string>
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"هل تريد إيقاف البث؟"</string>
@@ -515,6 +521,10 @@
     <string name="communal_widget_picker_title" msgid="1953369090475731663">"التطبيقات المصغّرة المصمَّمة لشاشة القفل"</string>
     <string name="communal_widget_picker_description" msgid="490515450110487871">"يمكن للجميع رؤية التطبيقات المصغّرة على شاشة القفل، حتى في حال قفل الجهاز اللوحي."</string>
     <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"إلغاء اختيار التطبيق المصغّر"</string>
+    <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
+    <skip />
+    <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
+    <skip />
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"التطبيقات المصغّرة المصمَّمة لشاشة القفل"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"لفتح تطبيق باستخدام تطبيق مصغَّر، عليك إثبات هويتك. يُرجى ملاحظة أنّ أي شخص يمكنه الاطّلاع محتوى التطبيقات المصغَّرة، حتى وإن كان جهازك اللوحي مُقفلاً. بعض التطبيقات المصغّرة قد لا تكون مُصمَّمة لإضافتها إلى شاشة القفل، وقد يكون هذا الإجراء غير آمن."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"حسنًا"</string>
@@ -553,8 +563,8 @@
     <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"هل تريد بث محتوى الشاشة؟"</string>
     <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"بث محتوى تطبيق واحد"</string>
     <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"بث محتوى الشاشة بالكامل"</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"أثناء بث محتوى الشاشة بالكامل، سيكون كل المحتوى المعروض على شاشتك مرئيًا. لذا يُرجى توخي الحذر بشأن المعلومات، مثل كلمات المرور وتفاصيل الدفع والرسائل والصور وملفات الصوت والفيديو."</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"أثناء بث محتوى تطبيق، سيكون كل المحتوى المعروض أو الذي يتم تشغيله في ذلك التطبيق مرئيًا. لذا يُرجى توخي الحذر بشأن المعلومات، مثل كلمات المرور وتفاصيل الدفع والرسائل والصور وملفات الصوت والفيديو."</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"سيتم بث كل المحتوى المعروض على شاشتك، لذا يُرجى توخي الحذر بشأن المعلومات الظاهرة، مثل كلمات المرور وتفاصيل الدفع والرسائل والصور والمقاطع الصوتية والفيديوهات."</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"سيتم بث كل المحتوى المعروض أو الذي يتم تشغيله في ذلك التطبيق، لذا يُرجى توخي الحذر بشأن المعلومات الظاهرة، مثل كلمات المرور وتفاصيل الدفع والرسائل والصور والمقاطع الصوتية والفيديوهات."</string>
     <string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"بث محتوى الشاشة"</string>
     <string name="media_projection_entry_cast_app_selector_title" msgid="6323062146661922387">"اختيار تطبيق لبث محتواه"</string>
     <string name="media_projection_entry_generic_permission_dialog_title" msgid="4519802931547483628">"هل تريد بدء المشاركة؟"</string>
@@ -571,6 +581,10 @@
     <string name="clear_all_notifications_text" msgid="348312370303046130">"محو الكل"</string>
     <string name="manage_notifications_text" msgid="6885645344647733116">"إدارة"</string>
     <string name="manage_notifications_history_text" msgid="57055985396576230">"السجلّ"</string>
+    <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
+    <skip />
+    <!-- no translation found for notification_history_button_description (1578657591405033383) -->
+    <skip />
     <string name="notification_section_header_incoming" msgid="850925217908095197">"الإشعارات الجديدة"</string>
     <string name="notification_section_header_gentle" msgid="6804099527336337197">"صامتة"</string>
     <string name="notification_section_header_alerting" msgid="5581175033680477651">"الإشعارات"</string>
@@ -1405,8 +1419,7 @@
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"رمز التوسيع"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"أو"</string>
     <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"مقبض السحب"</string>
-    <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) -->
-    <skip />
+    <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"إعدادات لوحة المفاتيح"</string>
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"التنقّل باستخدام لوحة المفاتيح"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"تعرَّف على اختصارات لوحة المفاتيح"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"التنقّل باستخدام لوحة اللمس"</string>
@@ -1464,8 +1477,6 @@
     <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"مقدَّمة من التطبيقات"</string>
     <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"العرض"</string>
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"غير معروفة"</string>
-    <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) -->
-    <skip />
-    <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) -->
-    <skip />
+    <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"إعادة ضبط المربّعات"</string>
+    <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"هل تريد إعادة ضبط المربّعات إلى ترتيبها وحجمها الأصليَّين؟"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-as/strings.xml b/packages/SystemUI/res/values-as/strings.xml
index c006caa..b71db2e 100644
--- a/packages/SystemUI/res/values-as/strings.xml
+++ b/packages/SystemUI/res/values-as/strings.xml
@@ -136,11 +136,17 @@
     <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"বৰ্তমান আপুনি <xliff:g id="APP_NAME">%1$s</xliff:g> ৰেকৰ্ড কৰি আছে"</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"ৰেকৰ্ডিং বন্ধ কৰক"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"স্ক্ৰীন শ্বেয়াৰ কৰি থকা হৈছে"</string>
+    <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
+    <skip />
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"স্ক্ৰীন শ্বেয়াৰ কৰা বন্ধ কৰিবনে?"</string>
+    <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
+    <skip />
     <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"বৰ্তমান আপুনি আপোনাৰ গোটেই স্ক্ৰীনখন <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>ৰ সৈতে শ্বেয়াৰ কৰি আছে"</string>
     <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"বৰ্তমান আপুনি আপোনাৰ গোটেই স্ক্ৰীনখন এটা এপৰ সৈতে শ্বেয়াৰ কৰি আছে"</string>
     <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"বৰ্তমান আপুনি <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> শ্বেয়াৰ কৰি আছে"</string>
     <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"বৰ্তমান আপুনি এটা এপ্ শ্বেয়াৰ কৰি আছে"</string>
+    <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
+    <skip />
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"শ্বেয়াৰ কৰা বন্ধ কৰক"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"স্ক্ৰীন কাষ্ট কৰি থকা হৈছে"</string>
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"কাষ্ট কৰা বন্ধ কৰিবনে?"</string>
@@ -515,6 +521,10 @@
     <string name="communal_widget_picker_title" msgid="1953369090475731663">"লক স্ক্ৰীনৰ ৱিজেট"</string>
     <string name="communal_widget_picker_description" msgid="490515450110487871">"আপোনাৰ টেবলেটটো লক কৰি ৰাখিলেও যিকোনো লোকে আপোনাৰ লক স্ক্ৰীনত ৱিজেট চাব পাৰে।"</string>
     <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"ৱিজেট বাছনিৰ পৰা আঁতৰাওক"</string>
+    <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
+    <skip />
+    <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
+    <skip />
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"লক স্ক্ৰীন ৱিজেট"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"এটা ৱিজেট ব্যৱহাৰ কৰি কোনো এপ্ খুলিবলৈ, এয়া আপুনিয়েই বুলি সত্যাপন পৰীক্ষা কৰিব লাগিব। লগতে, মনত ৰাখিব যে যিকোনো লোকেই সেইবোৰ চাব পাৰে, আনকি আপোনাৰ টেবলেটটো লক হৈ থাকিলেও। কিছুমান ৱিজেট হয়তো আপোনাৰ লক স্ক্ৰীনৰ বাবে কৰা হোৱা নাই আৰু ইয়াত যোগ কৰাটো অসুৰক্ষিত হ’ব পাৰে।"</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"বুজি পালোঁ"</string>
@@ -571,6 +581,10 @@
     <string name="clear_all_notifications_text" msgid="348312370303046130">"আটাইবোৰ মচক"</string>
     <string name="manage_notifications_text" msgid="6885645344647733116">"পৰিচালনা কৰক"</string>
     <string name="manage_notifications_history_text" msgid="57055985396576230">"ইতিহাস"</string>
+    <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
+    <skip />
+    <!-- no translation found for notification_history_button_description (1578657591405033383) -->
+    <skip />
     <string name="notification_section_header_incoming" msgid="850925217908095197">"নতুন"</string>
     <string name="notification_section_header_gentle" msgid="6804099527336337197">"নীৰৱ"</string>
     <string name="notification_section_header_alerting" msgid="5581175033680477651">"জাননীসমূহ"</string>
@@ -1405,8 +1419,7 @@
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"বিস্তাৰ কৰাৰ চিহ্ন"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"অথবা"</string>
     <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"ড্ৰেগ হেণ্ডেল"</string>
-    <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) -->
-    <skip />
+    <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"কীব’ৰ্ডৰ ছেটিং"</string>
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"কীব’ৰ্ড ব্যৱহাৰ কৰি নেভিগে’ট কৰক"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"কীব’ৰ্ডৰ শ্বৰ্টকাটসমূহৰ বিষয়ে জানক"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"আপোনাৰ টাচ্চপেড ব্যৱহাৰ কৰি নেভিগে’ট কৰক"</string>
@@ -1438,7 +1451,7 @@
     <string name="home_controls_dream_label" msgid="6567105701292324257">"ঘৰৰ সা-সৰঞ্জামৰ নিয়ন্ত্ৰণ"</string>
     <string name="home_controls_dream_description" msgid="4644150952104035789">"স্ক্ৰীনছেভাৰ হিচাপে ক্ষিপ্ৰতাৰে ঘৰৰ সা-সৰঞ্জামৰ নিয়ন্ত্ৰণ এক্সেছ কৰক"</string>
     <string name="volume_undo_action" msgid="5815519725211877114">"আনডু কৰক"</string>
-    <string name="back_edu_toast_content" msgid="4530314597378982956">"উভতি যাবলৈ টাচ্চপেডখনত তিনিটা আঙুলিৰে বাওঁ বা সোঁফালে ছোৱাইপ কৰক"</string>
+    <string name="back_edu_toast_content" msgid="4530314597378982956">"উভতি যাবলৈ, টাচ্চপেডখনত তিনিটা আঙুলিৰে বাওঁ বা সোঁফালে ছোৱাইপ কৰক"</string>
     <string name="home_edu_toast_content" msgid="3381071147871955415">"গৃহপৃষ্ঠালৈ যাওক, টাচ্চপেডত তিনিটা আঙুলিৰে ওপৰলৈ ছোৱাইপ কৰক"</string>
     <string name="overview_edu_toast_content" msgid="5797030644017804518">"শেহতীয়া এপ্‌সমূহ চাবলৈ টাচ্চপেডখনত তিনিটা আঙুলিৰে ওপৰলৈ ছোৱাইপ কৰি ধৰি ৰাখক"</string>
     <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"আপোনাৰ আটাইবোৰ এপ্‌ চাবলৈ আপোনাৰ কীব’ৰ্ডৰ কাৰ্য কীটোত টিপক"</string>
@@ -1464,8 +1477,6 @@
     <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"এপে প্ৰদান কৰা"</string>
     <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"ডিছপ্লে’"</string>
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"অজ্ঞাত"</string>
-    <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) -->
-    <skip />
-    <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) -->
-    <skip />
+    <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"টাইল ৰিছেট কৰক"</string>
+    <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"টাইলসমূহ সেইসমূহৰ মূল ক্ৰম আৰু আকাৰলৈ ৰিছেট কৰিবনে?"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml
index f33fa77..d51c9aa 100644
--- a/packages/SystemUI/res/values-az/strings.xml
+++ b/packages/SystemUI/res/values-az/strings.xml
@@ -136,11 +136,17 @@
     <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Hazırda <xliff:g id="APP_NAME">%1$s</xliff:g> tətbiqini çəkirsiniz"</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Qeydəalmanı dayandırın"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Ekran paylaşılır"</string>
+    <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
+    <skip />
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Ekran paylaşımı dayandırılsın?"</string>
+    <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
+    <skip />
     <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Hazırda bütün ekranı <xliff:g id="HOST_APP_NAME">%1$s</xliff:g> ilə paylaşırsınız"</string>
     <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Hazırda bütün ekranı tətbiq ilə paylaşırsınız"</string>
     <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"Hazırda <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> paylaşırsınız"</string>
     <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"Hazırda tətbiq paylaşırsınız"</string>
+    <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
+    <skip />
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Paylaşımı dayandırın"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Ekran yayımlanır"</string>
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Yayım dayandırılsın?"</string>
@@ -515,6 +521,10 @@
     <string name="communal_widget_picker_title" msgid="1953369090475731663">"Kilid ekranı vidcetləri"</string>
     <string name="communal_widget_picker_description" msgid="490515450110487871">"Planşet kilidli olsa belə, hər kəs kilid ekranınızdakı vidcetlərə baxa bilər."</string>
     <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"vidcet seçimini silin"</string>
+    <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
+    <skip />
+    <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
+    <skip />
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Kilid ekranı vidcetləri"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Vidcetdən istifadə edərək tətbiqi açmaq üçün kimliyi doğrulamalısınız. Planşet kilidli olsa da, hər kəs vidcetlərə baxa bilər. Bəzi vidcetlər kilid ekranı üçün nəzərdə tutulmayıb və bura əlavə etmək təhlükəli ola bilər."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Anladım"</string>
@@ -571,6 +581,10 @@
     <string name="clear_all_notifications_text" msgid="348312370303046130">"Hamısını silin"</string>
     <string name="manage_notifications_text" msgid="6885645344647733116">"İdarə edin"</string>
     <string name="manage_notifications_history_text" msgid="57055985396576230">"Tarixçə"</string>
+    <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
+    <skip />
+    <!-- no translation found for notification_history_button_description (1578657591405033383) -->
+    <skip />
     <string name="notification_section_header_incoming" msgid="850925217908095197">"Yeni"</string>
     <string name="notification_section_header_gentle" msgid="6804099527336337197">"Səssiz"</string>
     <string name="notification_section_header_alerting" msgid="5581175033680477651">"Bildirişlər"</string>
@@ -1405,8 +1419,7 @@
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"İkonanı genişləndirin"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"və ya"</string>
     <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Dəstəyi çəkin"</string>
-    <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) -->
-    <skip />
+    <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Klaviatura ayarları"</string>
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Klaviaturadan istifadə edərək hərəkət edin"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Klaviatura qısayolları haqqında öyrənin"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Taçpeddən istifadə edərək hərəkət edin"</string>
@@ -1464,8 +1477,6 @@
     <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Tətbiqlər tərəfindən təmin edilir"</string>
     <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Displey"</string>
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Naməlum"</string>
-    <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) -->
-    <skip />
-    <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) -->
-    <skip />
+    <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"Mozaikləri sıfırlayın"</string>
+    <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"Mozaiklər orijinal sıra və ölçülərinə sıfırlansın?"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
index 11db758..881495f 100644
--- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml
+++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
@@ -72,7 +72,7 @@
     <string name="usb_disable_contaminant_detection" msgid="3827082183595978641">"Omogući USB"</string>
     <string name="learn_more" msgid="4690632085667273811">"Saznajte više"</string>
     <string name="global_action_screenshot" msgid="2760267567509131654">"Snimak ekrana"</string>
-    <string name="global_action_smart_lock_disabled" msgid="6286551337177954859">"Produženo otključavanje je onemogućeno"</string>
+    <string name="global_action_smart_lock_disabled" msgid="6286551337177954859">"Produženo otključano je onemogućeno"</string>
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"je poslao/la sliku"</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"Čuvanje snimka ekrana..."</string>
     <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"Snimak ekrana se čuva na poslovnom profilu…"</string>
@@ -136,11 +136,17 @@
     <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Trenutno snimate: <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Zaustavi snimanje"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Ekran se deli"</string>
+    <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
+    <skip />
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Želite da zaustavite deljenje ekrana?"</string>
+    <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
+    <skip />
     <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Trenutno delite ceo ekran sa: <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>"</string>
     <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Trenutno delite ceo ekran sa aplikacijom"</string>
     <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"Trenutno delite: <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>"</string>
     <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"Trenutno delite aplikaciju"</string>
+    <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
+    <skip />
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Zaustavi deljenje"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Prebacuje se ekran"</string>
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Želite da zaustavite prebacivanje?"</string>
@@ -515,6 +521,10 @@
     <string name="communal_widget_picker_title" msgid="1953369090475731663">"Vidžeti za zaključani ekran"</string>
     <string name="communal_widget_picker_description" msgid="490515450110487871">"Svi mogu da vide vidžete na zaključanom ekranu, čak i kada je tablet zaključan."</string>
     <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"poništi izbor vidžeta"</string>
+    <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
+    <skip />
+    <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
+    <skip />
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Vidžeti za zaključani ekran"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Da biste otvorili aplikaciju koja koristi vidžet, treba da potvrdite da ste to vi. Imajte u vidu da svako može da ga vidi, čak i kada je tablet zaključan. Neki vidžeti možda nisu namenjeni za zaključani ekran i možda nije bezbedno da ih tamo dodate."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Važi"</string>
@@ -555,7 +565,7 @@
     <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"Prebaci ceo ekran"</string>
     <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"Kada prebacujete ceo ekran, vidi se sve što je na njemu. Zato pazite na lozinke, informacije o plaćanju, poruke, slike, audio i video sadržaj."</string>
     <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"Kada prebacujete aplikaciju, vidi se sav sadržaj koji se prikazuje ili pušta u njoj. Zato pazite na lozinke, informacije o plaćanju, poruke, slike, audio i video sadržaj."</string>
-    <string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"Prebacii ekran"</string>
+    <string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"Prebaci ekran"</string>
     <string name="media_projection_entry_cast_app_selector_title" msgid="6323062146661922387">"Odaberite aplikaciju koju želite da prebacite"</string>
     <string name="media_projection_entry_generic_permission_dialog_title" msgid="4519802931547483628">"Želite da počnete da delite?"</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_entire_screen" msgid="5407906851409410209">"Kada delite, snimate ili prebacujete, Android ima pristup kompletnom sadržaju koji je vidljiv na ekranu ili se pušta na uređaju. Zato pazite na lozinke, informacije o plaćanju, poruke, slike, i audio i video sadržaj."</string>
@@ -571,6 +581,10 @@
     <string name="clear_all_notifications_text" msgid="348312370303046130">"Obriši sve"</string>
     <string name="manage_notifications_text" msgid="6885645344647733116">"Upravljaj"</string>
     <string name="manage_notifications_history_text" msgid="57055985396576230">"Istorija"</string>
+    <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
+    <skip />
+    <!-- no translation found for notification_history_button_description (1578657591405033383) -->
+    <skip />
     <string name="notification_section_header_incoming" msgid="850925217908095197">"Novo"</string>
     <string name="notification_section_header_gentle" msgid="6804099527336337197">"Nečujno"</string>
     <string name="notification_section_header_alerting" msgid="5581175033680477651">"Obaveštenja"</string>
@@ -849,13 +863,13 @@
     <string name="group_system_access_all_apps_search" msgid="1553588630154197469">"Otvori listu aplikacija"</string>
     <string name="group_system_access_system_settings" msgid="8731721963449070017">"Otvori podešavanja"</string>
     <string name="group_system_access_google_assistant" msgid="7210074957915968110">"Otvori Pomoćnik"</string>
-    <string name="group_system_lock_screen" msgid="7391191300363416543">"Zaključavanje ekrana"</string>
+    <string name="group_system_lock_screen" msgid="7391191300363416543">"Otključavanje ekrana"</string>
     <string name="group_system_quick_memo" msgid="3764560265935722903">"Napravi belešku"</string>
     <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Obavljanje više zadataka istovremeno"</string>
-    <string name="system_multitasking_rhs" msgid="8714224917276297810">"Koristite podeljeni ekran sa aktuelnom aplikacijom s desne strane"</string>
-    <string name="system_multitasking_lhs" msgid="8402954791206308783">"Koristite podeljeni ekran sa aktuelnom aplikacijom s leve strane"</string>
+    <string name="system_multitasking_rhs" msgid="8714224917276297810">"Koristi podeljeni ekran sa tom aplikacijom s desne strane"</string>
+    <string name="system_multitasking_lhs" msgid="8402954791206308783">"Koristi podeljeni ekran sa tom aplikacijom s leve strane"</string>
     <string name="system_multitasking_full_screen" msgid="336048080383640562">"Pređi sa podeljenog ekrana na ceo ekran"</string>
-    <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Pređite u aplikaciju zdesna ili ispod dok koristite podeljeni ekran"</string>
+    <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Pređi u aplikaciju zdesna ili ispod dok je podeljen ekran"</string>
     <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Pređite u aplikaciju sleva ili iznad dok koristite podeljeni ekran"</string>
     <string name="system_multitasking_replace" msgid="7410071959803642125">"U režimu podeljenog ekrana: zamena jedne aplikacije drugom"</string>
     <string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Unos"</string>
@@ -1405,8 +1419,7 @@
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ikona za proširivanje"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ili"</string>
     <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Marker za prevlačenje"</string>
-    <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) -->
-    <skip />
+    <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Podešavanja tastature"</string>
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Krećite se pomoću tastature"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Saznajte više o tasterskim prečicama"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Krećite se pomoću tačpeda"</string>
@@ -1438,14 +1451,14 @@
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Kontrole za dom"</string>
     <string name="home_controls_dream_description" msgid="4644150952104035789">"Brz pristup kontrolama za dom kao čuvaru ekrana"</string>
     <string name="volume_undo_action" msgid="5815519725211877114">"Opozovi"</string>
-    <string name="back_edu_toast_content" msgid="4530314597378982956">"Da biste se vratili, prevucite ulevo ili udesno sa tri prsta na tačpedu"</string>
+    <string name="back_edu_toast_content" msgid="4530314597378982956">"Za nazad, prevucite ulevo ili udesno sa tri prsta na tačpedu"</string>
     <string name="home_edu_toast_content" msgid="3381071147871955415">"Da biste otišli na početni ekran, prevucite nagore sa tri prsta na tačpedu"</string>
     <string name="overview_edu_toast_content" msgid="5797030644017804518">"Da biste pregledali nedavne aplikacije, prevucite nagore i zadržite sa tri prsta na tačpedu"</string>
     <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"Da biste pogledali sve aplikacije, pritisnite taster radnji na tastaturi"</string>
     <string name="redacted_notification_single_line_title" msgid="212019960919261670">"Redigovano"</string>
     <string name="redacted_notification_single_line_text" msgid="8684166405005242945">"Otključajte za prikaz"</string>
     <string name="contextual_education_dialog_title" msgid="4630392552837487324">"Kontekstualno obrazovanje"</string>
-    <string name="back_edu_notification_title" msgid="5624780717751357278">"Koristite tačped da biste se vratili"</string>
+    <string name="back_edu_notification_title" msgid="5624780717751357278">"Koristite tačped za vraćanje nazad"</string>
     <string name="back_edu_notification_content" msgid="2497557451540954068">"Prevucite ulevo ili udesno sa tri prsta. Dodirnite da biste videli više pokreta."</string>
     <string name="home_edu_notification_title" msgid="6097902076909654045">"Koristite tačped da biste otišli na početni ekran"</string>
     <string name="home_edu_notification_content" msgid="6631697734535766588">"Prevucite nagore sa tri prsta. Dodirnite da biste videli više pokreta."</string>
@@ -1464,8 +1477,6 @@
     <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Obezbeđuju aplikacije"</string>
     <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Ekran"</string>
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Nepoznato"</string>
-    <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) -->
-    <skip />
-    <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) -->
-    <skip />
+    <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"Resetujte pločice"</string>
+    <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"Želite da resetujete pločice na prvobitni redosled i veličine?"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml
index 8ba951f..16bb70c 100644
--- a/packages/SystemUI/res/values-be/strings.xml
+++ b/packages/SystemUI/res/values-be/strings.xml
@@ -136,11 +136,17 @@
     <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Зараз вы запісваеце змесціва праграмы \"<xliff:g id="APP_NAME">%1$s</xliff:g>\""</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Спыніць запіс"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Экран абагульваецца"</string>
+    <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
+    <skip />
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Спыніць абагульванне экрана?"</string>
+    <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
+    <skip />
     <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Зараз вы абагульваеце змесціва ўсяго экрана з праграмай \"<xliff:g id="HOST_APP_NAME">%1$s</xliff:g>\""</string>
     <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Зараз вы абагульваеце змесціва ўсяго экрана з праграмай"</string>
     <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"Зараз вы абагульваеце змесціва праграмы \"<xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>\""</string>
     <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"Зараз вы абагульваеце змесціва праграмы"</string>
+    <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
+    <skip />
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Спыніць абагульванне"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Экран трансліруецца"</string>
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Спыніць трансляцыю?"</string>
@@ -515,6 +521,10 @@
     <string name="communal_widget_picker_title" msgid="1953369090475731663">"Віджэты на экране блакіроўкі"</string>
     <string name="communal_widget_picker_description" msgid="490515450110487871">"Віджэты на экране блакіроўкі будуць бачныя, нават калі планшэт заблакіраваны."</string>
     <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"скасаваць выбар віджэта"</string>
+    <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
+    <skip />
+    <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
+    <skip />
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Віджэты на экране блакіроўкі"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Каб адкрыць праграму з дапамогай віджэта, вам неабходна будзе пацвердзіць сваю асобу. Таксама памятайце, што такія віджэты могуць пабачыць іншыя людзі, нават калі экран планшэта заблакіраваны. Некаторыя віджэты могуць не падыходзіць для выкарыстання на экране блакіроўкі, і дадаваць іх сюды можа быць небяспечна."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Зразумела"</string>
@@ -571,6 +581,10 @@
     <string name="clear_all_notifications_text" msgid="348312370303046130">"Ачысціць усё"</string>
     <string name="manage_notifications_text" msgid="6885645344647733116">"Кіраваць"</string>
     <string name="manage_notifications_history_text" msgid="57055985396576230">"Гісторыя"</string>
+    <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
+    <skip />
+    <!-- no translation found for notification_history_button_description (1578657591405033383) -->
+    <skip />
     <string name="notification_section_header_incoming" msgid="850925217908095197">"Новае"</string>
     <string name="notification_section_header_gentle" msgid="6804099527336337197">"Без гуку"</string>
     <string name="notification_section_header_alerting" msgid="5581175033680477651">"Апавяшчэнні"</string>
@@ -1405,8 +1419,7 @@
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Значок \"Разгарнуць\""</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"або"</string>
     <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Маркер перацягвання"</string>
-    <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) -->
-    <skip />
+    <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Налады клавіятуры"</string>
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Навігацыя з дапамогай клавіятуры"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Азнаёмцеся са спалучэннямі клавіш"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Навігацыя з дапамогай сэнсарнай панэлі"</string>
@@ -1464,8 +1477,6 @@
     <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Забяспечваюцца праграмамі"</string>
     <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Экран"</string>
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Невядома"</string>
-    <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) -->
-    <skip />
-    <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) -->
-    <skip />
+    <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"Скінуць пліткі"</string>
+    <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"Скінуць пліткі да зыходнага парадку і памеру?"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml
index 71649cf..2b3a0b9 100644
--- a/packages/SystemUI/res/values-bg/strings.xml
+++ b/packages/SystemUI/res/values-bg/strings.xml
@@ -136,11 +136,17 @@
     <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"В момента записвате <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Спиране на записа"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Екранът се споделя"</string>
+    <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
+    <skip />
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Да се спре ли споделянето на екрана?"</string>
+    <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
+    <skip />
     <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"В момента споделяте целия си екран с(ъс) <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>"</string>
     <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"В момента споделяте целия си екран с приложение"</string>
     <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"В момента споделяте <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>"</string>
     <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"В момента споделяте приложение"</string>
+    <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
+    <skip />
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Спиране на споделянето"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Екранът се предава"</string>
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Да се спре ли предаването?"</string>
@@ -515,6 +521,10 @@
     <string name="communal_widget_picker_title" msgid="1953369090475731663">"Приспособления за заключения екран"</string>
     <string name="communal_widget_picker_description" msgid="490515450110487871">"Всеки ще вижда приспособленията на закл. екран дори ако таблетът ви е заключен."</string>
     <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"премахване на избора от приспособлението"</string>
+    <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
+    <skip />
+    <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
+    <skip />
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Приспособления за заключения екран"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"За да отворите дадено приложение посредством приспособление, ще трябва да потвърдите, че това сте вие. Също така имайте предвид, че всеки ще вижда приспособленията дори когато таблетът ви е заключен. Възможно е някои от тях да не са предназначени за заключения екран и добавянето им на него може да е опасно."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Разбрах"</string>
@@ -571,6 +581,10 @@
     <string name="clear_all_notifications_text" msgid="348312370303046130">"Изчистване на всички"</string>
     <string name="manage_notifications_text" msgid="6885645344647733116">"Управление"</string>
     <string name="manage_notifications_history_text" msgid="57055985396576230">"История"</string>
+    <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
+    <skip />
+    <!-- no translation found for notification_history_button_description (1578657591405033383) -->
+    <skip />
     <string name="notification_section_header_incoming" msgid="850925217908095197">"Нови"</string>
     <string name="notification_section_header_gentle" msgid="6804099527336337197">"Беззвучни"</string>
     <string name="notification_section_header_alerting" msgid="5581175033680477651">"Известия"</string>
@@ -1405,8 +1419,7 @@
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Икона за разгъване"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"или"</string>
     <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Манипулатор за преместване с плъзгане"</string>
-    <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) -->
-    <skip />
+    <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Настройки на клавиатурата"</string>
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Навигирайте посредством клавиатурата си"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Научете за клавишните комбинации"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Навигирайте посредством сензорния панел"</string>
diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml
index 92056c9..f39a644 100644
--- a/packages/SystemUI/res/values-bn/strings.xml
+++ b/packages/SystemUI/res/values-bn/strings.xml
@@ -136,11 +136,17 @@
     <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"আপনি বর্তমানে <xliff:g id="APP_NAME">%1$s</xliff:g> রেকর্ড করছেন"</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"রেকর্ড করা বন্ধ করুন"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"স্ক্রিন শেয়ার করা হচ্ছে"</string>
+    <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
+    <skip />
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"স্ক্রিন শেয়ার করা বন্ধ করবেন?"</string>
+    <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
+    <skip />
     <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"আপনি বর্তমানে <xliff:g id="HOST_APP_NAME">%1$s</xliff:g> অ্যাপের সাথে আপনার সম্পূর্ণ স্ক্রিন শেয়ার করছেন"</string>
     <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"আপনি বর্তমানে কোনও একটি অ্যাপের সাথে আপনার সম্পূর্ণ স্ক্রিন শেয়ার করছেন"</string>
     <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"আপনি বর্তমানে <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> অ্যাপের সাথে শেয়ার করছেন"</string>
     <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"আপনি বর্তমানে কোনও একটি অ্যাপের সাথে শেয়ার করছেন"</string>
+    <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
+    <skip />
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"শেয়ার করা বন্ধ করুন"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"স্ক্রিন কাস্ট করা হচ্ছে"</string>
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"কাস্ট করা বন্ধ করবেন?"</string>
@@ -515,6 +521,10 @@
     <string name="communal_widget_picker_title" msgid="1953369090475731663">"লক স্ক্রিন উইজেট"</string>
     <string name="communal_widget_picker_description" msgid="490515450110487871">"আপনার ট্যাবলেট লক থাকলেও যেকোনও ব্যক্তি লক স্ক্রিনে উইজেট দেখতে পাবেন।"</string>
     <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"উইজেট বাদ দিন"</string>
+    <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
+    <skip />
+    <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
+    <skip />
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"লক স্ক্রিন উইজেট"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"উইজেট ব্যবহার করে কোনও অ্যাপ খুলতে, আপনাকে নিজের পরিচয় যাচাই করতে হবে। এছাড়াও, মনে রাখবেন, আপনার ট্যাবলেট লক থাকলেও যেকেউ তা দেখতে পারবেন। কিছু উইজেট আপনার লক স্ক্রিনের উদ্দেশ্যে তৈরি করা হয়নি এবং এখানে যোগ করা নিরাপদ নাও হতে পারে।"</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"বুঝেছি"</string>
@@ -571,6 +581,10 @@
     <string name="clear_all_notifications_text" msgid="348312370303046130">"সব মুছে দিন"</string>
     <string name="manage_notifications_text" msgid="6885645344647733116">"ম্যানেজ করুন"</string>
     <string name="manage_notifications_history_text" msgid="57055985396576230">"ইতিহাস"</string>
+    <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
+    <skip />
+    <!-- no translation found for notification_history_button_description (1578657591405033383) -->
+    <skip />
     <string name="notification_section_header_incoming" msgid="850925217908095197">"নতুন"</string>
     <string name="notification_section_header_gentle" msgid="6804099527336337197">"আওয়াজ করবে না"</string>
     <string name="notification_section_header_alerting" msgid="5581175033680477651">"বিজ্ঞপ্তি"</string>
@@ -799,8 +813,8 @@
     <string name="keyboard_key_back" msgid="4185420465469481999">"ফিরুন"</string>
     <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string>
     <string name="keyboard_key_space" msgid="6980847564173394012">"Space"</string>
-    <string name="keyboard_key_enter" msgid="8633362970109751646">"এন্টার"</string>
-    <string name="keyboard_key_backspace" msgid="4095278312039628074">"ব্যাকস্পেস"</string>
+    <string name="keyboard_key_enter" msgid="8633362970109751646">"Enter"</string>
+    <string name="keyboard_key_backspace" msgid="4095278312039628074">"Backspace"</string>
     <string name="keyboard_key_media_play_pause" msgid="8389984232732277478">"প্লে/বিরতি"</string>
     <string name="keyboard_key_media_stop" msgid="1509943745250377699">"থামান"</string>
     <string name="keyboard_key_media_next" msgid="8502476691227914952">"পরবর্তী"</string>
@@ -1405,8 +1419,7 @@
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"আইকন বড় করুন"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"অথবা"</string>
     <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"টেনে আনার হ্যান্ডেল"</string>
-    <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) -->
-    <skip />
+    <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"কীবোর্ড সেটিংস"</string>
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"আপনার কীবোর্ড ব্যবহার করে নেভিগেট করুন"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"কীবোর্ড শর্টকাট সম্পর্কে জানুন"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"আপনার টাচপ্যাড ব্যবহার করে নেভিগেট করুন"</string>
@@ -1464,8 +1477,6 @@
     <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"অ্যাপের তরফ থেকে দেওয়া"</string>
     <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"ডিসপ্লে"</string>
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"অজানা"</string>
-    <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) -->
-    <skip />
-    <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) -->
-    <skip />
+    <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"টাইল রিসেট করুন"</string>
+    <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"টাইলগুলিকে অরিজিনাল অর্ডার ও সাইজ অনুযায়ী রিসেট করবেন?"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml
index a02b604..ec57c6a 100644
--- a/packages/SystemUI/res/values-bs/strings.xml
+++ b/packages/SystemUI/res/values-bs/strings.xml
@@ -136,11 +136,17 @@
     <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Trenutno snimate aplikaciju <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Zaustavi snimanje"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Dijeljenje ekrana"</string>
+    <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
+    <skip />
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Zaustaviti dijeljenje ekrana?"</string>
+    <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
+    <skip />
     <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Trenutno dijelite cijeli ekran s aplikacijom <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>"</string>
     <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Trenutno dijelite cijeli ekran s aplikacijom"</string>
     <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"Trenutno dijelite aplikaciju <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>"</string>
     <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"Trenutno dijelite aplikaciju"</string>
+    <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
+    <skip />
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Zaustavi dijeljenje"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Emitiranje ekrana"</string>
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Zaustaviti emitiranje?"</string>
@@ -515,6 +521,10 @@
     <string name="communal_widget_picker_title" msgid="1953369090475731663">"Vidžeti na zaključanom ekranu"</string>
     <string name="communal_widget_picker_description" msgid="490515450110487871">"Svi mogu pregledati vidžete na zaključanom ekranu, čak i ako je tablet zaključan."</string>
     <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"poništavanje odabira vidžeta"</string>
+    <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
+    <skip />
+    <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
+    <skip />
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Vidžeti na zaključanom ekranu"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Da otvorite aplikaciju pomoću vidžeta, morat ćete potvrditi identitet. Također imajte na umu da ih svako može pregledati, čak i ako je tablet zaključan. Neki vidžeti možda nisu namijenjeni za vaš zaključani ekran i njihovo dodavanje ovdje možda nije sigurno."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Razumijem"</string>
@@ -571,6 +581,10 @@
     <string name="clear_all_notifications_text" msgid="348312370303046130">"Obriši sve"</string>
     <string name="manage_notifications_text" msgid="6885645344647733116">"Upravljajte"</string>
     <string name="manage_notifications_history_text" msgid="57055985396576230">"Historija"</string>
+    <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
+    <skip />
+    <!-- no translation found for notification_history_button_description (1578657591405033383) -->
+    <skip />
     <string name="notification_section_header_incoming" msgid="850925217908095197">"Novo"</string>
     <string name="notification_section_header_gentle" msgid="6804099527336337197">"Nečujno"</string>
     <string name="notification_section_header_alerting" msgid="5581175033680477651">"Obavještenja"</string>
@@ -844,12 +858,12 @@
     <string name="group_system_go_back" msgid="2730322046244918816">"Nazad"</string>
     <string name="group_system_access_home_screen" msgid="4130366993484706483">"Odlazak na početni ekran"</string>
     <string name="group_system_overview_open_apps" msgid="5659958952937994104">"Prikaz nedavnih aplikacija"</string>
-    <string name="group_system_cycle_forward" msgid="5478663965957647805">"Kruženje kroz nedavne aplikacije unaprijed"</string>
-    <string name="group_system_cycle_back" msgid="8194102916946802902">"Kruženje kroz nedavne aplikacije unazad"</string>
+    <string name="group_system_cycle_forward" msgid="5478663965957647805">"Kruženje unaprijed kroz nedavne aplikacije"</string>
+    <string name="group_system_cycle_back" msgid="8194102916946802902">"Kruženje unazad kroz nedavne aplikacije"</string>
     <string name="group_system_access_all_apps_search" msgid="1553588630154197469">"Otvaranje liste aplikacija"</string>
     <string name="group_system_access_system_settings" msgid="8731721963449070017">"Otvaranje postavki"</string>
     <string name="group_system_access_google_assistant" msgid="7210074957915968110">"Otvaranje Asistenta"</string>
-    <string name="group_system_lock_screen" msgid="7391191300363416543">"Zaključani ekran"</string>
+    <string name="group_system_lock_screen" msgid="7391191300363416543">"Zaključavanje ekrana"</string>
     <string name="group_system_quick_memo" msgid="3764560265935722903">"Pisanje bilješke"</string>
     <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Multitasking"</string>
     <string name="system_multitasking_rhs" msgid="8714224917276297810">"Korištenje podijeljenog ekrana s trenutnom aplikacijom na desnoj strani"</string>
@@ -1405,8 +1419,7 @@
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ikona proširivanja"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ili"</string>
     <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Ručica za prevlačenje"</string>
-    <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) -->
-    <skip />
+    <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Postavke tastature"</string>
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Krećite se pomoću tastature"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Saznajte više o prečicama tastature"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Krećite se pomoću dodirne podloge"</string>
@@ -1446,13 +1459,13 @@
     <string name="redacted_notification_single_line_text" msgid="8684166405005242945">"Otključajte da pregledate"</string>
     <string name="contextual_education_dialog_title" msgid="4630392552837487324">"Kontekstualno obrazovanje"</string>
     <string name="back_edu_notification_title" msgid="5624780717751357278">"Koristite dodirnu podlogu da se vratite"</string>
-    <string name="back_edu_notification_content" msgid="2497557451540954068">"Prevucite ulijevo ili udesno s tri prsta. Dodirnite da saznate za više pokreta."</string>
+    <string name="back_edu_notification_content" msgid="2497557451540954068">"Prevucite ulijevo ili udesno s tri prsta. Dodirnite da naučite više pokreta."</string>
     <string name="home_edu_notification_title" msgid="6097902076909654045">"Koristite dodirnu podlogu da se vratite na početnu stranicu"</string>
-    <string name="home_edu_notification_content" msgid="6631697734535766588">"Prevucite nagore s tri prsta. Dodirnite da saznate za više pokreta."</string>
+    <string name="home_edu_notification_content" msgid="6631697734535766588">"Prevucite nagore s tri prsta. Dodirnite da naučite više pokreta."</string>
     <string name="overview_edu_notification_title" msgid="1265824157319562406">"Koristite dodirnu podlogu da pregledate nedavne aplikacije"</string>
-    <string name="overview_edu_notification_content" msgid="3578204677648432500">"Prevucite nagore i zadržite s tri prsta. Dodirnite da saznate za više pokreta."</string>
+    <string name="overview_edu_notification_content" msgid="3578204677648432500">"Prevucite nagore i zadržite s tri prsta. Dodirnite da naučite više pokreta."</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Koristite tastaturu da pregledate sve aplikacije"</string>
-    <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Pritisnite tipku radnji bilo kada. Dodirnite da saznate za više pokreta."</string>
+    <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Pritisnite tipku radnji bilo kada. Dodirnite da naučite više pokreta."</string>
     <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="910988771011857460">"Dodatno zatamnjenje je sada dio klizača za osvijetljenost"</string>
     <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="4453123359258743230">"Sada možete dodatno zatamniti ekran daljnjim smanjenjem nivoa osvijetljenosti.\n\nBudući da je ova funkcija sada dio klizača za osvijetljenost, prečice za dodatno zatamnjenje se uklanjaju."</string>
     <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="3947537827396916005">"Ukloni prečice za dodatno zatamnjenje"</string>
@@ -1464,8 +1477,6 @@
     <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Pružaju aplikacije"</string>
     <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Prikaz"</string>
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Nepoznato"</string>
-    <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) -->
-    <skip />
-    <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) -->
-    <skip />
+    <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"Vratite kartice na zadano"</string>
+    <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"Vratiti kartice na zadani redoslijed i veličine?"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index 7ea0236..9c9ba90 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -136,11 +136,17 @@
     <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Ara mateix estàs gravant <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Atura la gravació"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"S\'està compartint la pantalla"</string>
+    <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
+    <skip />
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Vols deixar de compartir la pantalla?"</string>
+    <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
+    <skip />
     <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Ara mateix estàs compartint tota la pantalla amb <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>"</string>
     <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Ara mateix estàs compartint tota la pantalla amb una aplicació"</string>
     <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"Ara mateix estàs compartint <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>"</string>
     <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"Ara mateix estàs compartint una aplicació"</string>
+    <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
+    <skip />
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Deixa de compartir"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"S\'està emetent la pantalla"</string>
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Vols aturar l\'emissió?"</string>
@@ -515,6 +521,10 @@
     <string name="communal_widget_picker_title" msgid="1953369090475731663">"Widgets de la pantalla de bloqueig"</string>
     <string name="communal_widget_picker_description" msgid="490515450110487871">"Tothom pot veure els widgets de la teva pantalla de bloqueig, fins i tot quan la tauleta està bloquejada."</string>
     <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"desselecciona el widget"</string>
+    <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
+    <skip />
+    <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
+    <skip />
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Widgets de la pantalla de bloqueig"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Per obrir una aplicació utilitzant un widget, necessitaràs verificar la teva identitat. També has de tenir en compte que qualsevol persona pot veure els widgets, fins i tot quan la tauleta està bloquejada. És possible que alguns widgets no estiguin pensats per a la pantalla de bloqueig i que no sigui segur afegir-los-hi."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Entesos"</string>
@@ -553,8 +563,8 @@
     <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Vols emetre la pantalla?"</string>
     <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Emet una aplicació"</string>
     <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"Emet tota la pantalla"</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"Quan emets tota la pantalla, qualsevol cosa que es mostra en pantalla és visible. Per aquest motiu, ves amb compte amb les contrasenyes, les dades de pagament, els missatges, les fotos, i l\'àudio i el vídeo."</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"Quan emets una aplicació, qualsevol cosa que es mostra o que es reprodueix en aquesta aplicació és visible. Per aquest motiu, ves amb compte amb les contrasenyes, les dades de pagament, els missatges, les fotos, i l\'àudio i el vídeo."</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"Quan emets tota la pantalla, qualsevol cosa que es mostra en pantalla és visible. Per aquest motiu, ves amb compte amb elements com les contrasenyes, les dades de pagament, els missatges, les fotos, i l\'àudio i el vídeo."</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"Quan emets una aplicació, qualsevol cosa que es mostra o que es reprodueix en aquesta aplicació és visible. Per aquest motiu, ves amb compte amb elements com les contrasenyes, les dades de pagament, els missatges, les fotos, i l\'àudio i el vídeo."</string>
     <string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"Emet la pantalla"</string>
     <string name="media_projection_entry_cast_app_selector_title" msgid="6323062146661922387">"Tria una aplicació per emetre"</string>
     <string name="media_projection_entry_generic_permission_dialog_title" msgid="4519802931547483628">"Vols començar a compartir?"</string>
@@ -571,6 +581,10 @@
     <string name="clear_all_notifications_text" msgid="348312370303046130">"Esborra-ho tot"</string>
     <string name="manage_notifications_text" msgid="6885645344647733116">"Gestiona"</string>
     <string name="manage_notifications_history_text" msgid="57055985396576230">"Historial"</string>
+    <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
+    <skip />
+    <!-- no translation found for notification_history_button_description (1578657591405033383) -->
+    <skip />
     <string name="notification_section_header_incoming" msgid="850925217908095197">"Novetats"</string>
     <string name="notification_section_header_gentle" msgid="6804099527336337197">"Silenciat"</string>
     <string name="notification_section_header_alerting" msgid="5581175033680477651">"Notificacions"</string>
@@ -1405,8 +1419,7 @@
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Desplega la icona"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"o"</string>
     <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Ansa per arrossegar"</string>
-    <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) -->
-    <skip />
+    <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Configuració del teclat"</string>
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Navega amb el teclat"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Aprèn les tecles de drecera"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Navega amb el ratolí tàctil"</string>
@@ -1464,8 +1477,6 @@
     <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Proporcionat per aplicacions"</string>
     <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Pantalla"</string>
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Desconegut"</string>
-    <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) -->
-    <skip />
-    <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) -->
-    <skip />
+    <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"Restableix les icones"</string>
+    <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"Vols restablir l\'ordre i les mides originals de les icones?"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index 750e75d..95f3699 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -112,8 +112,8 @@
     <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Pořídit nahrávku obrazovky?"</string>
     <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Nahrát jednu aplikaci"</string>
     <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Nahrát celou obrazovku"</string>
-    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Při nahrávání celé obrazovky se zaznamenává veškerý obsah na obrazovce. Buďte proto opatrní s věcmi, jako jsou hesla, platební údaje, zprávy, fotografie, zvuk a video."</string>
-    <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Při nahrávání aplikace se zaznamenává všechno, co se v dané obrazovce zobrazuje nebo přehrává. Buďte proto opatrní s věcmi, jako jsou hesla, platební údaje, zprávy, fotografie, zvuk a video."</string>
+    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Při nahrávání celé obrazovky se zaznamenává veškerý obsah na obrazovce. Buďte proto opatrní, když jde o hesla, platební údaje, zprávy, fotografie, zvuk a video."</string>
+    <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Při nahrávání aplikace se zaznamenává všechno, co se v dané obrazovce zobrazuje nebo přehrává. Buďte proto opatrní, když jde o hesla, platební údaje, zprávy, fotografie, zvuk a video."</string>
     <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Nahrát obrazovku"</string>
     <string name="screenrecord_app_selector_title" msgid="3854492366333954736">"Vyberte aplikaci, ze které chcete pořídit nahrávku"</string>
     <string name="screenrecord_audio_label" msgid="6183558856175159629">"Nahrávat zvuk"</string>
@@ -136,11 +136,17 @@
     <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Momentálně nahráváte aplikaci <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Ukončit nahrávání"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Sdílení obrazovky"</string>
+    <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
+    <skip />
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Ukončit sdílení obrazovky?"</string>
+    <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
+    <skip />
     <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Momentálně sdílíte celou obrazovku s aplikací <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>"</string>
     <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Momentálně sdílíte celou obrazovku s aplikací"</string>
     <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"Momentálně sdílíte aplikaci <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>"</string>
     <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"Momentálně sdílíte aplikaci"</string>
+    <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
+    <skip />
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Ukončit sdílení"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Odesílání obsahu obrazovky"</string>
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Ukončit odesílání?"</string>
@@ -515,6 +521,10 @@
     <string name="communal_widget_picker_title" msgid="1953369090475731663">"Widgety na obrazovce uzamčení"</string>
     <string name="communal_widget_picker_description" msgid="490515450110487871">"Widgety na obrazovce uzamčení může zobrazit kdokoli, i když je tablet uzamčen."</string>
     <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"zrušit výběr widgetu"</string>
+    <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
+    <skip />
+    <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
+    <skip />
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Widgety na obrazovce uzamčení"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"K otevření aplikace pomocí widgetu budete muset ověřit svou totožnost. Také mějte na paměti, že widgety uvidí kdokoli, i když tablet bude uzamčen. Některé widgety nemusí být pro obrazovku uzamčení určeny a nemusí být bezpečné je na ni přidat."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Rozumím"</string>
@@ -545,20 +555,20 @@
     <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Sdílet celou obrazovku"</string>
     <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
     <skip />
-    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Při sdílení celé obrazovky vidí aplikace <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> vše, co se na obrazovce nachází nebo děje. Buďte proto opatrní s věcmi, jako jsou hesla, platební údaje, zprávy, fotografie, zvuk a video."</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Při sdílení aplikace vidí aplikace <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> vše, co se ve sdílené aplikaci nachází nebo děje. Buďte proto opatrní s věcmi, jako jsou hesla, platební údaje, zprávy, fotografie, zvuk a video."</string>
+    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Při sdílení celé obrazovky vidí aplikace <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> vše, co se na obrazovce nachází nebo děje. Buďte proto opatrní, když jde o hesla, platební údaje, zprávy, fotografie, zvuk a video."</string>
+    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Při sdílení aplikace vidí aplikace <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> vše, co se ve sdílené aplikaci nachází nebo děje. Buďte proto opatrní, když jde o hesla, platební údaje, zprávy, fotografie, zvuk a video."</string>
     <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Sdílet obrazovku"</string>
     <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"Aplikace <xliff:g id="APP_NAME">%1$s</xliff:g> tuto možnost zakázala"</string>
     <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Vyberte aplikaci ke sdílení"</string>
     <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Odeslat obrazovku?"</string>
     <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Odeslat jednu aplikaci"</string>
     <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"Odeslat celou obrazovku"</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"Při odesílání celé obrazovky je vidět vše, co se na obrazovce nachází nebo děje. Buďte proto opatrní s věcmi, jako jsou hesla, platební údaje, zprávy, fotografie, zvuk a video."</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"Při odesílání aplikace je vidět vše, co se v aplikaci nachází nebo děje. Buďte proto opatrní s věcmi, jako jsou hesla, platební údaje, zprávy, fotografie, zvuk a video."</string>
-    <string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"Odesílání obrazovky"</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"Při odesílání celé obrazovky je vidět vše, co se na obrazovce nachází nebo děje. Buďte proto opatrní, když jde o hesla, platební údaje, zprávy, fotografie, zvuk a video."</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"Při odesílání aplikace je vidět vše, co se v aplikaci nachází nebo děje. Buďte proto opatrní, když jde o hesla, platební údaje, zprávy, fotografie, zvuk a video."</string>
+    <string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"Odeslat obrazovku"</string>
     <string name="media_projection_entry_cast_app_selector_title" msgid="6323062146661922387">"Vyberte aplikaci k odesílání"</string>
     <string name="media_projection_entry_generic_permission_dialog_title" msgid="4519802931547483628">"Začít sdílet?"</string>
-    <string name="media_projection_entry_generic_permission_dialog_warning_entire_screen" msgid="5407906851409410209">"Během sdílení, nahrávání nebo odesílání má Android přístup k veškerému obsahu, který je viditelný na obrazovce nebo se přehrává v zařízení. Buďte proto opatrní s věcmi, jako jsou hesla, platební údaje, zprávy, fotografie, zvuk a video."</string>
+    <string name="media_projection_entry_generic_permission_dialog_warning_entire_screen" msgid="5407906851409410209">"Během sdílení, nahrávání nebo odesílání má Android přístup k veškerému obsahu, který je viditelný na obrazovce nebo se přehrává v zařízení. Buďte proto opatrní, když jde o hesla, platební údaje, zprávy, fotografie, zvuk a video."</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_single_app" msgid="3454859977888159495">"Během sdílení, nahrávání nebo odesílání aplikace má Android přístup k veškerému obsahu, který je v dané aplikaci zobrazen nebo přehráván. Buďte proto opatrní s informacemi, jako jsou hesla, platební údaje, zprávy, fotky, zvukové záznamy a videa."</string>
     <string name="media_projection_entry_generic_permission_dialog_continue" msgid="8640381403048097116">"Začít"</string>
     <string name="media_projection_entry_generic_permission_dialog_continue_single_app" msgid="5920814988611877051">"Další"</string>
@@ -571,6 +581,10 @@
     <string name="clear_all_notifications_text" msgid="348312370303046130">"Smazat vše"</string>
     <string name="manage_notifications_text" msgid="6885645344647733116">"Spravovat"</string>
     <string name="manage_notifications_history_text" msgid="57055985396576230">"Historie"</string>
+    <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
+    <skip />
+    <!-- no translation found for notification_history_button_description (1578657591405033383) -->
+    <skip />
     <string name="notification_section_header_incoming" msgid="850925217908095197">"Nové"</string>
     <string name="notification_section_header_gentle" msgid="6804099527336337197">"Tichý režim"</string>
     <string name="notification_section_header_alerting" msgid="5581175033680477651">"Oznámení"</string>
@@ -1405,8 +1419,7 @@
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ikona rozbalení"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"nebo"</string>
     <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Úchyt pro přetažení"</string>
-    <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) -->
-    <skip />
+    <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Nastavení klávesnice"</string>
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Navigujte pomocí klávesnice"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Naučte se klávesové zkratky"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Navigujte pomocí touchpadu"</string>
@@ -1464,8 +1477,6 @@
     <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Poskytováno aplikacemi"</string>
     <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Displej"</string>
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Neznámé"</string>
-    <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) -->
-    <skip />
-    <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) -->
-    <skip />
+    <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"Resetování dlaždic"</string>
+    <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"Resetovat dlaždice na původní pořadí a velikosti?"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index b52b182..8266799 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -136,11 +136,17 @@
     <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Du optager i øjeblikket <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Stop optagelse"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Skærmen deles"</string>
+    <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
+    <skip />
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Vil du stoppe skærmdelingen?"</string>
+    <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
+    <skip />
     <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Du deler i øjeblikket hele skærmen med <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>"</string>
     <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Du deler i øjeblikket hele skærmen med en app"</string>
     <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"Du deler i øjeblikket <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>"</string>
     <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"Du deler i øjeblikket en app"</string>
+    <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
+    <skip />
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Stop deling"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Skærmen castes"</string>
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Vil du stoppe din cast?"</string>
@@ -515,6 +521,10 @@
     <string name="communal_widget_picker_title" msgid="1953369090475731663">"Widgets på låseskærmen"</string>
     <string name="communal_widget_picker_description" msgid="490515450110487871">"Alle kan se widgets på din låseskærm, også selvom din tablet er låst."</string>
     <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"fjern markering af widget"</string>
+    <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
+    <skip />
+    <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
+    <skip />
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Widgets på låseskærmen"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Hvis du vil åbne en app ved hjælp af en widget, skal du verificere din identitet. Husk også, at alle kan se dem, også når din tablet er låst. Nogle widgets er muligvis ikke beregnet til låseskærmen, og det kan være usikkert at tilføje dem her."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"OK"</string>
@@ -571,6 +581,10 @@
     <string name="clear_all_notifications_text" msgid="348312370303046130">"Ryd alle"</string>
     <string name="manage_notifications_text" msgid="6885645344647733116">"Administrer"</string>
     <string name="manage_notifications_history_text" msgid="57055985396576230">"Historik"</string>
+    <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
+    <skip />
+    <!-- no translation found for notification_history_button_description (1578657591405033383) -->
+    <skip />
     <string name="notification_section_header_incoming" msgid="850925217908095197">"Nye"</string>
     <string name="notification_section_header_gentle" msgid="6804099527336337197">"Lydløs"</string>
     <string name="notification_section_header_alerting" msgid="5581175033680477651">"Notifikationer"</string>
@@ -1405,8 +1419,7 @@
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ikon for Udvid"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"eller"</string>
     <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Håndtag"</string>
-    <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) -->
-    <skip />
+    <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Tastaturindstillinger"</string>
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Naviger ved hjælp af dit tastatur"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Se tastaturgenveje"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Naviger ved hjælp af din touchplade"</string>
@@ -1464,8 +1477,6 @@
     <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Fra apps"</string>
     <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Skærm"</string>
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Ukendt"</string>
-    <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) -->
-    <skip />
-    <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) -->
-    <skip />
+    <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"Nulstil felter"</string>
+    <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"Vil du nulstille felterne til deres oprindelige rækkefølge og størrelser?"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml
index ba6452f..a4bc364 100644
--- a/packages/SystemUI/res/values-de/strings.xml
+++ b/packages/SystemUI/res/values-de/strings.xml
@@ -136,11 +136,17 @@
     <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Du zeichnest momentan Inhalte der App <xliff:g id="APP_NAME">%1$s</xliff:g> auf"</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Aufzeichnung beenden"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Bildschirm wird geteilt"</string>
+    <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
+    <skip />
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Bildschirmfreigabe beenden?"</string>
+    <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
+    <skip />
     <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Du teilst momentan deinen gesamten Bildschirm mit der App <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>"</string>
     <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Du teilst momentan deinen gesamten Bildschirm mit einer App"</string>
     <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"Du teilst momentan die App <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>"</string>
     <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"Du teilst momentan Inhalte einer App"</string>
+    <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
+    <skip />
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Freigabe beenden"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Bildschirm wird übertragen"</string>
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Streaming beenden?"</string>
@@ -515,6 +521,10 @@
     <string name="communal_widget_picker_title" msgid="1953369090475731663">"Sperrbildschirm-Widgets"</string>
     <string name="communal_widget_picker_description" msgid="490515450110487871">"Jeder kann Widgets auf deinem Sperrbildschirm sehen, auch bei gesperrtem Tablet."</string>
     <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"Auswahl für Widget aufheben"</string>
+    <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
+    <skip />
+    <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
+    <skip />
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Sperrbildschirm-Widgets"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Wenn du eine App mit einem Widget öffnen möchtest, musst du deine Identität bestätigen. Beachte auch, dass jeder die Widgets sehen kann, auch wenn dein Tablet gesperrt ist. Einige Widgets sind möglicherweise nicht für den Sperrbildschirm vorgesehen, sodass es unsicher sein kann, sie hier hinzuzufügen."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Ok"</string>
@@ -571,6 +581,10 @@
     <string name="clear_all_notifications_text" msgid="348312370303046130">"Alle löschen"</string>
     <string name="manage_notifications_text" msgid="6885645344647733116">"Verwalten"</string>
     <string name="manage_notifications_history_text" msgid="57055985396576230">"Verlauf"</string>
+    <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
+    <skip />
+    <!-- no translation found for notification_history_button_description (1578657591405033383) -->
+    <skip />
     <string name="notification_section_header_incoming" msgid="850925217908095197">"Neu"</string>
     <string name="notification_section_header_gentle" msgid="6804099527336337197">"Lautlos"</string>
     <string name="notification_section_header_alerting" msgid="5581175033680477651">"Benachrichtigungen"</string>
@@ -797,8 +811,8 @@
     <string name="keyboard_key_button_template" msgid="8005673627272051429">"Taste <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="keyboard_key_home" msgid="3734400625170020657">"Pos1"</string>
     <string name="keyboard_key_back" msgid="4185420465469481999">"Zurück"</string>
-    <string name="keyboard_key_tab" msgid="4592772350906496730">"Tabulatortaste"</string>
-    <string name="keyboard_key_space" msgid="6980847564173394012">"Leertaste"</string>
+    <string name="keyboard_key_tab" msgid="4592772350906496730">"Tabulator­taste"</string>
+    <string name="keyboard_key_space" msgid="6980847564173394012">"Leer­taste"</string>
     <string name="keyboard_key_enter" msgid="8633362970109751646">"Eingabetaste"</string>
     <string name="keyboard_key_backspace" msgid="4095278312039628074">"Rücktaste"</string>
     <string name="keyboard_key_media_play_pause" msgid="8389984232732277478">"Wiedergabe/Pause"</string>
@@ -840,7 +854,7 @@
     <string name="keyboard_shortcut_a11y_filter_current_app" msgid="7944592357493737911">"Tastenkombinationen für die aktuelle App werden angezeigt"</string>
     <string name="group_system_access_notification_shade" msgid="1619028907006553677">"Benachrichtigungen ansehen"</string>
     <string name="group_system_full_screenshot" msgid="5742204844232667785">"Screenshot erstellen"</string>
-    <string name="group_system_access_system_app_shortcuts" msgid="8562482996626694026">"Tastenkombinationen anzeigen"</string>
+    <string name="group_system_access_system_app_shortcuts" msgid="8562482996626694026">"Tasten­kombinationen anzeigen"</string>
     <string name="group_system_go_back" msgid="2730322046244918816">"Zurück"</string>
     <string name="group_system_access_home_screen" msgid="4130366993484706483">"Zum Startbildschirm wechseln"</string>
     <string name="group_system_overview_open_apps" msgid="5659958952937994104">"Letzte Apps aufrufen"</string>
@@ -849,7 +863,7 @@
     <string name="group_system_access_all_apps_search" msgid="1553588630154197469">"Liste der Apps öffnen"</string>
     <string name="group_system_access_system_settings" msgid="8731721963449070017">"Einstellungen öffnen"</string>
     <string name="group_system_access_google_assistant" msgid="7210074957915968110">"Assistant öffnen"</string>
-    <string name="group_system_lock_screen" msgid="7391191300363416543">"Sperrbildschirm"</string>
+    <string name="group_system_lock_screen" msgid="7391191300363416543">"Bildschirm sperren"</string>
     <string name="group_system_quick_memo" msgid="3764560265935722903">"Notiz machen"</string>
     <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Multitasking"</string>
     <string name="system_multitasking_rhs" msgid="8714224917276297810">"Splitscreen mit der aktuellen App auf der rechten Seite nutzen"</string>
@@ -1405,8 +1419,7 @@
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Symbol „Maximieren“"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"oder"</string>
     <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Ziehpunkt"</string>
-    <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) -->
-    <skip />
+    <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Tastatureinstellungen"</string>
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Navigation mit der Tastatur"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Informationen zu Tastenkombinationen"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Navigation mit dem Touchpad"</string>
@@ -1445,7 +1458,7 @@
     <string name="redacted_notification_single_line_title" msgid="212019960919261670">"Entfernt"</string>
     <string name="redacted_notification_single_line_text" msgid="8684166405005242945">"Zum Ansehen entsperren"</string>
     <string name="contextual_education_dialog_title" msgid="4630392552837487324">"Kontextbezogene Informationen"</string>
-    <string name="back_edu_notification_title" msgid="5624780717751357278">"Über das Touchpad zurückgehen"</string>
+    <string name="back_edu_notification_title" msgid="5624780717751357278">"Zum Zurückgehen Touchpad verwenden"</string>
     <string name="back_edu_notification_content" msgid="2497557451540954068">"Wische mit drei Fingern nach links oder rechts. Tippe für mehr Infos zu Touch-Gesten."</string>
     <string name="home_edu_notification_title" msgid="6097902076909654045">"Über das Touchpad zum Startbildschirm zurückkehren"</string>
     <string name="home_edu_notification_content" msgid="6631697734535766588">"Wische mit drei Fingern nach oben. Tippe für mehr Infos zu Touch-Gesten."</string>
@@ -1464,8 +1477,6 @@
     <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Von Apps bereitgestellt"</string>
     <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Display"</string>
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Unbekannt"</string>
-    <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) -->
-    <skip />
-    <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) -->
-    <skip />
+    <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"Kacheln zurücksetzen"</string>
+    <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"Kacheln auf die ursprüngliche Reihenfolge und Größe zurücksetzen?"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml
index 4cccb5a..278a8ea 100644
--- a/packages/SystemUI/res/values-el/strings.xml
+++ b/packages/SystemUI/res/values-el/strings.xml
@@ -136,11 +136,17 @@
     <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Αυτή τη στιγμή εγγράφετε το <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Διακοπή εγγραφής"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Γίνεται κοινοποίηση οθόνης"</string>
+    <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
+    <skip />
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Διακοπή κοινής χρήσης οθόνης;"</string>
+    <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
+    <skip />
     <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Αυτή τη στιγμή μοιράζεστε ολόκληρη την οθόνη σας με το <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>"</string>
     <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Αυτή τη στιγμή μοιράζεστε ολόκληρη την οθόνη σας με μια εφαρμογή"</string>
     <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"Αυτή τη στιγμή μοιράζεστε το <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>"</string>
     <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"Αυτή τη στιγμή μοιράζεστε μια εφαρμογή"</string>
+    <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
+    <skip />
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Διακοπή κοινής χρήσης"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Μετάδοση οθόνης"</string>
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Τερματισμός μετάδοσης;"</string>
@@ -515,6 +521,10 @@
     <string name="communal_widget_picker_title" msgid="1953369090475731663">"Γραφικά στοιχεία οθόνης κλειδώματος"</string>
     <string name="communal_widget_picker_description" msgid="490515450110487871">"Όλοι μπορούν να δουν γραφικά στοιχεία στην οθόνη κλειδώματος, ακόμα και αν το tablet είναι κλειδωμένο."</string>
     <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"αποεπιλογή γραφικού στοιχείου"</string>
+    <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
+    <skip />
+    <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
+    <skip />
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Γραφικά στοιχεία οθόνης κλειδώματος"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Για να ανοίξετε μια εφαρμογή χρησιμοποιώντας ένα γραφικό στοιχείο, θα πρέπει να επαληθεύσετε την ταυτότητά σας. Επίσης, λάβετε υπόψη ότι η προβολή τους είναι δυνατή από οποιονδήποτε, ακόμα και όταν το tablet σας είναι κλειδωμένο. Ορισμένα γραφικά στοιχεία μπορεί να μην προορίζονται για την οθόνη κλειδώματος και η προσθήκη τους εδώ ενδέχεται να μην είναι ασφαλής."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Το κατάλαβα"</string>
@@ -571,6 +581,10 @@
     <string name="clear_all_notifications_text" msgid="348312370303046130">"Διαγραφή όλων"</string>
     <string name="manage_notifications_text" msgid="6885645344647733116">"Διαχείριση"</string>
     <string name="manage_notifications_history_text" msgid="57055985396576230">"Ιστορικό"</string>
+    <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
+    <skip />
+    <!-- no translation found for notification_history_button_description (1578657591405033383) -->
+    <skip />
     <string name="notification_section_header_incoming" msgid="850925217908095197">"Νέα"</string>
     <string name="notification_section_header_gentle" msgid="6804099527336337197">"Σίγαση"</string>
     <string name="notification_section_header_alerting" msgid="5581175033680477651">"Ειδοποιήσεις"</string>
@@ -1394,7 +1408,7 @@
     <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Πολυδιεργασία"</string>
     <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"Πρόσφατες εφαρμογές"</string>
     <string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Διαχωρισμός οθόνης"</string>
-    <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Είσοδος"</string>
+    <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Εισαγωγή"</string>
     <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Συντομεύσεις εφαρμογών"</string>
     <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Τρέχουσα εφαρμογή"</string>
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Προσβασιμότητα"</string>
@@ -1405,8 +1419,7 @@
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Εικονίδιο ανάπτυξης"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ή"</string>
     <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Λαβή μεταφοράς"</string>
-    <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) -->
-    <skip />
+    <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Ρυθμίσεις πληκτρολογίου"</string>
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Πλοήγηση με το πληκτρολόγιο"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Μάθετε συντομεύσεις πληκτρολογίου"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Πλοήγηση με την επιφάνεια αφής"</string>
@@ -1464,8 +1477,6 @@
     <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Παρέχεται από εφαρμογές"</string>
     <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Προβολή"</string>
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Άγνωστο"</string>
-    <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) -->
-    <skip />
-    <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) -->
-    <skip />
+    <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"Επαναφορά πλακιδίων"</string>
+    <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"Επαναφορά των πλακιδίων στην αρχική τους σειρά και μεγέθη;"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml
index e96dd8c..2fbd60a 100644
--- a/packages/SystemUI/res/values-en-rAU/strings.xml
+++ b/packages/SystemUI/res/values-en-rAU/strings.xml
@@ -136,11 +136,17 @@
     <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"You\'re currently recording <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Stop recording"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Sharing screen"</string>
+    <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
+    <skip />
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Stop sharing screen?"</string>
+    <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
+    <skip />
     <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"You\'re currently sharing your entire screen with <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>"</string>
     <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"You\'re currently sharing your entire screen with an app"</string>
     <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"You\'re currently sharing <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>"</string>
     <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"You\'re currently sharing an app"</string>
+    <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
+    <skip />
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Stop sharing"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Casting screen"</string>
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Stop casting?"</string>
@@ -515,6 +521,10 @@
     <string name="communal_widget_picker_title" msgid="1953369090475731663">"Lock screen widgets"</string>
     <string name="communal_widget_picker_description" msgid="490515450110487871">"Anyone can view widgets on your lock screen, even if your tablet\'s locked."</string>
     <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"unselect widget"</string>
+    <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
+    <skip />
+    <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
+    <skip />
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Lock screen widgets"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"To open an app using a widget, you\'ll need to verify that it\'s you. Also, bear in mind that anyone can view them, even when your tablet\'s locked. Some widgets may not have been intended for your lock screen and may be unsafe to add here."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Got it"</string>
@@ -571,6 +581,10 @@
     <string name="clear_all_notifications_text" msgid="348312370303046130">"Clear all"</string>
     <string name="manage_notifications_text" msgid="6885645344647733116">"Manage"</string>
     <string name="manage_notifications_history_text" msgid="57055985396576230">"History"</string>
+    <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
+    <skip />
+    <!-- no translation found for notification_history_button_description (1578657591405033383) -->
+    <skip />
     <string name="notification_section_header_incoming" msgid="850925217908095197">"New"</string>
     <string name="notification_section_header_gentle" msgid="6804099527336337197">"Silent"</string>
     <string name="notification_section_header_alerting" msgid="5581175033680477651">"Notifications"</string>
@@ -1405,8 +1419,7 @@
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Expand icon"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"or"</string>
     <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Drag handle"</string>
-    <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) -->
-    <skip />
+    <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Keyboard settings"</string>
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Navigate using your keyboard"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Learn keyboards shortcuts"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Navigate using your touchpad"</string>
@@ -1464,8 +1477,6 @@
     <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Provided by apps"</string>
     <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Display"</string>
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Unknown"</string>
-    <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) -->
-    <skip />
-    <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) -->
-    <skip />
+    <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"Reset tiles"</string>
+    <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"Reset tiles to their original order and sizes?"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-en-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml
index eee14f8..23475fa 100644
--- a/packages/SystemUI/res/values-en-rCA/strings.xml
+++ b/packages/SystemUI/res/values-en-rCA/strings.xml
@@ -136,11 +136,17 @@
     <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"You\'re currently recording <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Stop recording"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Sharing screen"</string>
+    <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
+    <skip />
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Stop sharing screen?"</string>
+    <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
+    <skip />
     <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"You\'re currently sharing your entire screen with <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>"</string>
     <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"You\'re currently sharing your entire screen with an app"</string>
     <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"You\'re currently sharing <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>"</string>
     <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"You\'re currently sharing an app"</string>
+    <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
+    <skip />
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Stop sharing"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Casting screen"</string>
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Stop casting?"</string>
@@ -515,6 +521,10 @@
     <string name="communal_widget_picker_title" msgid="1953369090475731663">"Lock screen widgets"</string>
     <string name="communal_widget_picker_description" msgid="490515450110487871">"Anyone can view widgets on your lock screen, even if your tablet\'s locked."</string>
     <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"unselect widget"</string>
+    <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
+    <skip />
+    <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
+    <skip />
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Lock screen widgets"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"To open an app using a widget, you’ll need to verify it’s you. Also, keep in mind that anyone can view them, even when your tablet’s locked. Some widgets may not have been intended for your lock screen and may be unsafe to add here."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Got it"</string>
@@ -571,6 +581,8 @@
     <string name="clear_all_notifications_text" msgid="348312370303046130">"Clear all"</string>
     <string name="manage_notifications_text" msgid="6885645344647733116">"Manage"</string>
     <string name="manage_notifications_history_text" msgid="57055985396576230">"History"</string>
+    <string name="notification_settings_button_description" msgid="2441994740884163889">"Notification settings"</string>
+    <string name="notification_history_button_description" msgid="1578657591405033383">"Notification history"</string>
     <string name="notification_section_header_incoming" msgid="850925217908095197">"New"</string>
     <string name="notification_section_header_gentle" msgid="6804099527336337197">"Silent"</string>
     <string name="notification_section_header_alerting" msgid="5581175033680477651">"Notifications"</string>
diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml
index e96dd8c..2fbd60a 100644
--- a/packages/SystemUI/res/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res/values-en-rGB/strings.xml
@@ -136,11 +136,17 @@
     <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"You\'re currently recording <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Stop recording"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Sharing screen"</string>
+    <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
+    <skip />
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Stop sharing screen?"</string>
+    <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
+    <skip />
     <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"You\'re currently sharing your entire screen with <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>"</string>
     <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"You\'re currently sharing your entire screen with an app"</string>
     <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"You\'re currently sharing <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>"</string>
     <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"You\'re currently sharing an app"</string>
+    <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
+    <skip />
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Stop sharing"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Casting screen"</string>
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Stop casting?"</string>
@@ -515,6 +521,10 @@
     <string name="communal_widget_picker_title" msgid="1953369090475731663">"Lock screen widgets"</string>
     <string name="communal_widget_picker_description" msgid="490515450110487871">"Anyone can view widgets on your lock screen, even if your tablet\'s locked."</string>
     <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"unselect widget"</string>
+    <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
+    <skip />
+    <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
+    <skip />
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Lock screen widgets"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"To open an app using a widget, you\'ll need to verify that it\'s you. Also, bear in mind that anyone can view them, even when your tablet\'s locked. Some widgets may not have been intended for your lock screen and may be unsafe to add here."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Got it"</string>
@@ -571,6 +581,10 @@
     <string name="clear_all_notifications_text" msgid="348312370303046130">"Clear all"</string>
     <string name="manage_notifications_text" msgid="6885645344647733116">"Manage"</string>
     <string name="manage_notifications_history_text" msgid="57055985396576230">"History"</string>
+    <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
+    <skip />
+    <!-- no translation found for notification_history_button_description (1578657591405033383) -->
+    <skip />
     <string name="notification_section_header_incoming" msgid="850925217908095197">"New"</string>
     <string name="notification_section_header_gentle" msgid="6804099527336337197">"Silent"</string>
     <string name="notification_section_header_alerting" msgid="5581175033680477651">"Notifications"</string>
@@ -1405,8 +1419,7 @@
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Expand icon"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"or"</string>
     <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Drag handle"</string>
-    <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) -->
-    <skip />
+    <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Keyboard settings"</string>
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Navigate using your keyboard"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Learn keyboards shortcuts"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Navigate using your touchpad"</string>
@@ -1464,8 +1477,6 @@
     <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Provided by apps"</string>
     <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Display"</string>
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Unknown"</string>
-    <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) -->
-    <skip />
-    <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) -->
-    <skip />
+    <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"Reset tiles"</string>
+    <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"Reset tiles to their original order and sizes?"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml
index e96dd8c..2fbd60a 100644
--- a/packages/SystemUI/res/values-en-rIN/strings.xml
+++ b/packages/SystemUI/res/values-en-rIN/strings.xml
@@ -136,11 +136,17 @@
     <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"You\'re currently recording <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Stop recording"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Sharing screen"</string>
+    <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
+    <skip />
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Stop sharing screen?"</string>
+    <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
+    <skip />
     <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"You\'re currently sharing your entire screen with <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>"</string>
     <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"You\'re currently sharing your entire screen with an app"</string>
     <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"You\'re currently sharing <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>"</string>
     <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"You\'re currently sharing an app"</string>
+    <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
+    <skip />
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Stop sharing"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Casting screen"</string>
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Stop casting?"</string>
@@ -515,6 +521,10 @@
     <string name="communal_widget_picker_title" msgid="1953369090475731663">"Lock screen widgets"</string>
     <string name="communal_widget_picker_description" msgid="490515450110487871">"Anyone can view widgets on your lock screen, even if your tablet\'s locked."</string>
     <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"unselect widget"</string>
+    <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
+    <skip />
+    <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
+    <skip />
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Lock screen widgets"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"To open an app using a widget, you\'ll need to verify that it\'s you. Also, bear in mind that anyone can view them, even when your tablet\'s locked. Some widgets may not have been intended for your lock screen and may be unsafe to add here."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Got it"</string>
@@ -571,6 +581,10 @@
     <string name="clear_all_notifications_text" msgid="348312370303046130">"Clear all"</string>
     <string name="manage_notifications_text" msgid="6885645344647733116">"Manage"</string>
     <string name="manage_notifications_history_text" msgid="57055985396576230">"History"</string>
+    <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
+    <skip />
+    <!-- no translation found for notification_history_button_description (1578657591405033383) -->
+    <skip />
     <string name="notification_section_header_incoming" msgid="850925217908095197">"New"</string>
     <string name="notification_section_header_gentle" msgid="6804099527336337197">"Silent"</string>
     <string name="notification_section_header_alerting" msgid="5581175033680477651">"Notifications"</string>
@@ -1405,8 +1419,7 @@
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Expand icon"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"or"</string>
     <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Drag handle"</string>
-    <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) -->
-    <skip />
+    <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Keyboard settings"</string>
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Navigate using your keyboard"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Learn keyboards shortcuts"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Navigate using your touchpad"</string>
@@ -1464,8 +1477,6 @@
     <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Provided by apps"</string>
     <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Display"</string>
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Unknown"</string>
-    <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) -->
-    <skip />
-    <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) -->
-    <skip />
+    <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"Reset tiles"</string>
+    <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"Reset tiles to their original order and sizes?"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index 97bd547..9ea1540 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -136,11 +136,17 @@
     <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Actualmente, estás grabando <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Detener grabación"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Compartiendo pantalla"</string>
+    <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
+    <skip />
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"¿Quieres dejar de compartir la pantalla?"</string>
+    <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
+    <skip />
     <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Actualmente, estás compartiendo toda la pantalla con <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>"</string>
     <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Actualmente, estás compartiendo toda la pantalla con una app"</string>
     <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"Actualmente, estás compartiendo <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>"</string>
     <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"Actualmente, estás compartiendo una app"</string>
+    <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
+    <skip />
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Dejar de compartir"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Transmitiendo pantalla"</string>
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"¿Detener la transmisión?"</string>
@@ -515,6 +521,10 @@
     <string name="communal_widget_picker_title" msgid="1953369090475731663">"Widgets en la pantalla de bloqueo"</string>
     <string name="communal_widget_picker_description" msgid="490515450110487871">"Los widgets de la pantalla de bloqueo podrán verse incluso si bloqueas la tablet."</string>
     <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"anular la selección del widget"</string>
+    <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
+    <skip />
+    <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
+    <skip />
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Widgets en la pantalla de bloqueo"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Para abrir una app usando un widget, debes verificar tu identidad. Además, ten en cuenta que cualquier persona podrá verlo, incluso cuando la tablet esté bloqueada. Es posible que algunos widgets no se hayan diseñados para la pantalla de bloqueo y podría ser peligroso agregarlos allí."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Entendido"</string>
@@ -571,6 +581,10 @@
     <string name="clear_all_notifications_text" msgid="348312370303046130">"Cerrar todo"</string>
     <string name="manage_notifications_text" msgid="6885645344647733116">"Administrar"</string>
     <string name="manage_notifications_history_text" msgid="57055985396576230">"Historial"</string>
+    <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
+    <skip />
+    <!-- no translation found for notification_history_button_description (1578657591405033383) -->
+    <skip />
     <string name="notification_section_header_incoming" msgid="850925217908095197">"Nuevo"</string>
     <string name="notification_section_header_gentle" msgid="6804099527336337197">"Silenciadas"</string>
     <string name="notification_section_header_alerting" msgid="5581175033680477651">"Notificaciones"</string>
@@ -1405,8 +1419,7 @@
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ícono de expandir"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"o"</string>
     <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Controlador de arrastre"</string>
-    <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) -->
-    <skip />
+    <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Configuración del teclado"</string>
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Navega con el teclado"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Aprende combinaciones de teclas"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Navega con el panel táctil"</string>
@@ -1464,8 +1477,6 @@
     <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Proporcionado por apps"</string>
     <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Pantalla"</string>
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Desconocido"</string>
-    <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) -->
-    <skip />
-    <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) -->
-    <skip />
+    <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"Restablecer tarjetas"</string>
+    <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"¿Quieres restablecer las tarjetas al orden y el tamaño originales?"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index 8d018bb..16ffd44 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -136,11 +136,17 @@
     <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Estás grabando <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Detener grabación"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Compartiendo pantalla"</string>
+    <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
+    <skip />
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"¿Dejar de compartir pantalla?"</string>
+    <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
+    <skip />
     <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Estás compartiendo toda tu pantalla con <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>"</string>
     <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Estás compartiendo toda tu pantalla con una aplicación"</string>
     <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"Estás compartiendo <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>"</string>
     <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"Estás compartiendo una aplicación"</string>
+    <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
+    <skip />
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Dejar de compartir"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Enviando pantalla"</string>
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"¿Dejar de enviar?"</string>
@@ -515,6 +521,10 @@
     <string name="communal_widget_picker_title" msgid="1953369090475731663">"Widgets para la pantalla de bloqueo"</string>
     <string name="communal_widget_picker_description" msgid="490515450110487871">"Cualquiera puede ver los widgets de tu pantalla de bloqueo, aunque tu tablet esté bloqueada."</string>
     <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"deseleccionar widget"</string>
+    <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
+    <skip />
+    <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
+    <skip />
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Widgets para la pantalla de bloqueo"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Para abrir una aplicación usando un widget, deberás verificar que eres tú. Además, ten en cuenta que cualquier persona podrá verlos, incluso aunque tu tablet esté bloqueada. Es posible que algunos widgets no estén pensados para la pantalla de bloqueo y no sea seguro añadirlos aquí."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Entendido"</string>
@@ -551,7 +561,7 @@
     <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> ha inhabilitado esta opción"</string>
     <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Elegir una aplicación para compartir"</string>
     <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"¿Enviar tu pantalla?"</string>
-    <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Enviar solo una aplicación"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Enviar una aplicación"</string>
     <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"Enviar toda la pantalla"</string>
     <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"Cuando envías toda tu pantalla, se ve todo lo que hay en ella. Debes tener cuidado con elementos como contraseñas, detalles de pagos, mensajes, fotos, audio y vídeo."</string>
     <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"Cuando envías una aplicación, se ve todo lo que se muestre o reproduzca en ella. Debes tener cuidado con elementos como contraseñas, detalles de pagos, mensajes, fotos, audio y vídeo."</string>
@@ -571,6 +581,10 @@
     <string name="clear_all_notifications_text" msgid="348312370303046130">"Borrar todo"</string>
     <string name="manage_notifications_text" msgid="6885645344647733116">"Gestionar"</string>
     <string name="manage_notifications_history_text" msgid="57055985396576230">"Historial"</string>
+    <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
+    <skip />
+    <!-- no translation found for notification_history_button_description (1578657591405033383) -->
+    <skip />
     <string name="notification_section_header_incoming" msgid="850925217908095197">"Nuevas"</string>
     <string name="notification_section_header_gentle" msgid="6804099527336337197">"Silenciadas"</string>
     <string name="notification_section_header_alerting" msgid="5581175033680477651">"Notificaciones"</string>
@@ -797,7 +811,7 @@
     <string name="keyboard_key_button_template" msgid="8005673627272051429">"Botón <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="keyboard_key_home" msgid="3734400625170020657">"Inicio"</string>
     <string name="keyboard_key_back" msgid="4185420465469481999">"Atrás"</string>
-    <string name="keyboard_key_tab" msgid="4592772350906496730">"Tabulador"</string>
+    <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string>
     <string name="keyboard_key_space" msgid="6980847564173394012">"Espacio"</string>
     <string name="keyboard_key_enter" msgid="8633362970109751646">"Intro"</string>
     <string name="keyboard_key_backspace" msgid="4095278312039628074">"Tecla de retroceso"</string>
@@ -855,7 +869,7 @@
     <string name="system_multitasking_rhs" msgid="8714224917276297810">"Usar la pantalla dividida con la aplicación actual a la derecha"</string>
     <string name="system_multitasking_lhs" msgid="8402954791206308783">"Usar la pantalla dividida con la aplicación actual a la izquierda"</string>
     <string name="system_multitasking_full_screen" msgid="336048080383640562">"Cambiar de pantalla dividida a pantalla completa"</string>
-    <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Cambiar a la app de la derecha o de abajo en pantalla dividida"</string>
+    <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Cambiar a la aplicación de la derecha o de abajo en pantalla dividida"</string>
     <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Cambiar a la app de la izquierda o de arriba en pantalla dividida"</string>
     <string name="system_multitasking_replace" msgid="7410071959803642125">"Con pantalla dividida: reemplazar una aplicación por otra"</string>
     <string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Entrada"</string>
@@ -1405,8 +1419,7 @@
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Icono de desplegar"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"o"</string>
     <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Controlador de arrastre"</string>
-    <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) -->
-    <skip />
+    <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Ajustes del teclado"</string>
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Desplázate con el teclado"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Aprende combinaciones de teclas"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Desplázate con el panel táctil"</string>
@@ -1464,8 +1477,6 @@
     <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Proporcionado por aplicaciones"</string>
     <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Pantalla"</string>
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Desconocido"</string>
-    <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) -->
-    <skip />
-    <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) -->
-    <skip />
+    <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"Restablecer recuadros"</string>
+    <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"¿Restablecer recuadros a su orden y tamaño originales?"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml
index 53651da..5c9664b 100644
--- a/packages/SystemUI/res/values-et/strings.xml
+++ b/packages/SystemUI/res/values-et/strings.xml
@@ -136,11 +136,17 @@
     <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Salvestate praegu rakendust <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Peata salvestamine"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Ekraani jagamine"</string>
+    <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
+    <skip />
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Kas lõpetada ekraanikuva jagamine?"</string>
+    <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
+    <skip />
     <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Jagate praegu kogu oma ekraanikuva rakendusega <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>"</string>
     <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Jagate praegu kogu oma ekraanikuva rakendusega"</string>
     <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"Jagate praegu rakenduse <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> kuva"</string>
     <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"Jagate praegu rakenduse kuva"</string>
+    <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
+    <skip />
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Lõpeta jagamine"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Ekraanikuva ülekandmine"</string>
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Kas peatada ülekandmine?"</string>
@@ -515,6 +521,10 @@
     <string name="communal_widget_picker_title" msgid="1953369090475731663">"Lukustuskuva vidinad"</string>
     <string name="communal_widget_picker_description" msgid="490515450110487871">"Igaüks saab vaadata luk.kuval olevaid vidinaid, isegi kui tahvelarvuti on lukus."</string>
     <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"tühistage vidina valimine"</string>
+    <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
+    <skip />
+    <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
+    <skip />
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Lukustuskuva vidinad"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Rakenduse avamiseks vidina abil peate kinnitama, et see olete teie. Samuti pidage meeles, et kõik saavad vidinaid vaadata, isegi kui teie tahvelarvuti on lukus. Mõni vidin ei pruugi olla ette nähtud teie lukustuskuva jaoks ja seda pole turvaline siia lisada."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Selge"</string>
@@ -571,6 +581,10 @@
     <string name="clear_all_notifications_text" msgid="348312370303046130">"Tühjenda kõik"</string>
     <string name="manage_notifications_text" msgid="6885645344647733116">"Haldamine"</string>
     <string name="manage_notifications_history_text" msgid="57055985396576230">"Ajalugu"</string>
+    <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
+    <skip />
+    <!-- no translation found for notification_history_button_description (1578657591405033383) -->
+    <skip />
     <string name="notification_section_header_incoming" msgid="850925217908095197">"Uued"</string>
     <string name="notification_section_header_gentle" msgid="6804099527336337197">"Hääletu"</string>
     <string name="notification_section_header_alerting" msgid="5581175033680477651">"Märguanded"</string>
@@ -1405,8 +1419,7 @@
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Laiendamisikoon"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"või"</string>
     <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Lohistamispide"</string>
-    <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) -->
-    <skip />
+    <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Klaviatuuri seaded"</string>
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Navigeerige klaviatuuri abil"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Õppige klaviatuuri otseteid"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Navigeerige puuteplaadi abil"</string>
@@ -1446,13 +1459,13 @@
     <string name="redacted_notification_single_line_text" msgid="8684166405005242945">"Vaatamiseks avage"</string>
     <string name="contextual_education_dialog_title" msgid="4630392552837487324">"Kontekstipõhised õpetused"</string>
     <string name="back_edu_notification_title" msgid="5624780717751357278">"Puuteplaadi kasutamine tagasiliikumiseks"</string>
-    <string name="back_edu_notification_content" msgid="2497557451540954068">"Pühkige kolme sõrmega vasakule või paremale. Puudutage žestide kohta lisateabe saamiseks."</string>
+    <string name="back_edu_notification_content" msgid="2497557451540954068">"Pühkige kolme sõrmega vasakule või paremale. Puudutage liigutuste kohta lisateabe saamiseks."</string>
     <string name="home_edu_notification_title" msgid="6097902076909654045">"Puuteplaadi kasutamine avakuvale liikumiseks"</string>
-    <string name="home_edu_notification_content" msgid="6631697734535766588">"Pühkige kolme sõrmega üles. Puudutage žestide kohta lisateabe saamiseks."</string>
+    <string name="home_edu_notification_content" msgid="6631697734535766588">"Pühkige kolme sõrmega üles. Puudutage liigutuste kohta lisateabe saamiseks."</string>
     <string name="overview_edu_notification_title" msgid="1265824157319562406">"Puuteplaadi kasutamine hiljutiste rakenduste kuvamiseks"</string>
-    <string name="overview_edu_notification_content" msgid="3578204677648432500">"Pühkige kolme sõrmega üles ja hoidke sõrmi plaadil. Puudutage žestide kohta lisateabe saamiseks."</string>
+    <string name="overview_edu_notification_content" msgid="3578204677648432500">"Pühkige kolme sõrmega üles ja hoidke sõrmi plaadil. Puudutage liigutuste kohta lisateabe saamiseks."</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Klaviatuuri kasutamine kõigi rakenduste kuvamiseks"</string>
-    <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Vajutage soovitud ajal toiminguklahvi. Puudutage žestide kohta lisateabe saamiseks."</string>
+    <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Vajutage soovitud ajal toiminguklahvi. Puudutage liigutuste kohta lisateabe saamiseks."</string>
     <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="910988771011857460">"Funktsioon „Eriti tume“ on nüüd osa ereduse liugurist"</string>
     <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="4453123359258743230">"Nüüd saate muuta ekraani eriti tumedaks, vähendades ereduse taset veelgi rohkem.\n\nKuna see funktsioon kuulub nüüd ereduse liugurisse, eemaldatakse funktsiooni „Eriti tume“ otseteed."</string>
     <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="3947537827396916005">"Eemalda funktsiooni „Eriti tume“ otseteed"</string>
@@ -1464,8 +1477,6 @@
     <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Rakendustelt"</string>
     <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Kuva"</string>
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Teadmata"</string>
-    <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) -->
-    <skip />
-    <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) -->
-    <skip />
+    <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"Paanide lähtestamine"</string>
+    <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"Kas soovite paanid lähtestada nende algsesse järjekorda ja suurusesse?"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml
index f739862..31348b6 100644
--- a/packages/SystemUI/res/values-eu/strings.xml
+++ b/packages/SystemUI/res/values-eu/strings.xml
@@ -136,11 +136,17 @@
     <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"<xliff:g id="APP_NAME">%1$s</xliff:g> grabatzen ari zara"</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Utzi grabatzeari"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Pantaila partekatzen"</string>
+    <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
+    <skip />
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Pantaila partekatzeari utzi nahi diozu?"</string>
+    <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
+    <skip />
     <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Pantaila osoa <xliff:g id="HOST_APP_NAME">%1$s</xliff:g> aplikazioarekin partekatzen ari zara"</string>
     <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Pantaila osoa aplikazio batekin partekatzen ari zara"</string>
     <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"<xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> partekatzen ari zara"</string>
     <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"Aplikazio bat partekatzen ari zara"</string>
+    <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
+    <skip />
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Utzi partekatzeari"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Pantaila igortzen"</string>
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Igortzeari utzi nahi diozu?"</string>
@@ -515,6 +521,10 @@
     <string name="communal_widget_picker_title" msgid="1953369090475731663">"Pantaila blokeatuko widgetak"</string>
     <string name="communal_widget_picker_description" msgid="490515450110487871">"Edonork ikus ditzake pantaila blokeatuko widgetak, tableta blokeatuta badago ere."</string>
     <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"desautatu widgeta"</string>
+    <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
+    <skip />
+    <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
+    <skip />
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Pantaila blokeatuko widgetak"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Aplikazio bat widget baten bidez irekitzeko, zeu zarela egiaztatu beharko duzu. Gainera, kontuan izan edonork ikusi ahalko dituela halako widgetak, tableta blokeatuta badago ere. Baliteke widget batzuk pantaila blokeaturako egokiak ez izatea, eta agian ez da segurua haiek bertan gehitzea."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Ados"</string>
@@ -554,9 +564,9 @@
     <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Igorri aplikazio bat"</string>
     <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"Igorri pantaila osoa"</string>
     <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"Pantaila osoa igortzen ari zarenean, pantailan duzun guztia dago ikusgai. Beraz, kontuz ibili pasahitzekin, ordainketen xehetasunekin, mezuekin, argazkiekin, audioekin eta bideoekin, besteak beste."</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"Aplikazio bat igortzen ari zarenean, aplikazio horretan agertzen den edo bertan erreproduzitzen ari den guztia dago ikusgai. Beraz, kontuz ibili pasahitzekin, ordainketen xehetasunekin, mezuekin, argazkiekin, audioekin eta bideoekin, besteak beste."</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"Aplikazio bat igortzen ari zarenean, aplikazio horretan agertzen edo erreproduzitzen den guztia dago ikusgai. Beraz, kontuz ibili pasahitzekin, ordainketen xehetasunekin, mezuekin, argazkiekin, audioekin eta bideoekin, besteak beste."</string>
     <string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"Igorri pantaila"</string>
-    <string name="media_projection_entry_cast_app_selector_title" msgid="6323062146661922387">"Aukeratu zer aplikazio igorri nahi duzun"</string>
+    <string name="media_projection_entry_cast_app_selector_title" msgid="6323062146661922387">"Aukeratu zein aplikazio igorri nahi duzun"</string>
     <string name="media_projection_entry_generic_permission_dialog_title" msgid="4519802931547483628">"Partekatzen hasi nahi duzu?"</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_entire_screen" msgid="5407906851409410209">"Edukia partekatzen, grabatzen edo igortzen ari zarenean, pantailan ikusgai dagoen edo gailuan erreproduzitzen ari den guztia atzi dezake Android-ek. Beraz, kontuz ibili pasahitzekin, ordainketen xehetasunekin, mezuekin, argazkiekin, audioekin eta bideoekin, besteak beste."</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_single_app" msgid="3454859977888159495">"Aplikazio bat partekatzen, grabatzen edo igortzen ari zarenean, aplikazio horretan ikusgai dagoen edo bertan erreproduzitzen ari den guztia atzi dezake Android-ek. Beraz, kontuz ibili pasahitzekin, ordainketen xehetasunekin, mezuekin, argazkiekin, audioekin eta bideoekin, besteak beste."</string>
@@ -571,6 +581,10 @@
     <string name="clear_all_notifications_text" msgid="348312370303046130">"Garbitu guztiak"</string>
     <string name="manage_notifications_text" msgid="6885645344647733116">"Kudeatu"</string>
     <string name="manage_notifications_history_text" msgid="57055985396576230">"Historia"</string>
+    <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
+    <skip />
+    <!-- no translation found for notification_history_button_description (1578657591405033383) -->
+    <skip />
     <string name="notification_section_header_incoming" msgid="850925217908095197">"Berria"</string>
     <string name="notification_section_header_gentle" msgid="6804099527336337197">"Isila"</string>
     <string name="notification_section_header_alerting" msgid="5581175033680477651">"Jakinarazpenak"</string>
@@ -1405,8 +1419,7 @@
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Zabaltzeko ikonoa"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"edo"</string>
     <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Arrastatzeko kontrol-puntua"</string>
-    <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) -->
-    <skip />
+    <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Teklatuaren ezarpenak"</string>
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Nabigatu teklatua erabilita"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Ikasi lasterbideak"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Nabigatu ukipen-panela erabilita"</string>
@@ -1464,8 +1477,6 @@
     <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Aplikazioenak"</string>
     <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Pantaila"</string>
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Ezezagunak"</string>
-    <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) -->
-    <skip />
-    <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) -->
-    <skip />
+    <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"Berrezarri lauzak"</string>
+    <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"Lauzen jatorrizko ordena eta tamainak berrezarri nahi dituzu?"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index 3d0b720..bc89200 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -136,11 +136,17 @@
     <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"اکنون درحال ضبط <xliff:g id="APP_NAME">%1$s</xliff:g> هستید"</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"توقف ضبط"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"درحال هم‌رسانی صفحه"</string>
+    <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
+    <skip />
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"هم‌رسانی صفحه متوقف شود؟"</string>
+    <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
+    <skip />
     <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"اکنون درحال هم‌رسانی کل صفحه‌نمایشتان با <xliff:g id="HOST_APP_NAME">%1$s</xliff:g> هستید"</string>
     <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"اکنون درحال هم‌رسانی کل صفحه‌نمایشتان با یک برنامه هستید"</string>
     <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"اکنون درحال هم‌رسانی <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> هستید"</string>
     <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"اکنون درحال هم‌رسانی با یک برنامه هستید"</string>
+    <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
+    <skip />
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"توقف هم‌رسانی"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"درحال پخش محتوای صفحه‌نمایش"</string>
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"پخش محتوا متوقف شود؟"</string>
@@ -515,6 +521,10 @@
     <string name="communal_widget_picker_title" msgid="1953369090475731663">"ابزاره‌های صفحه قفل"</string>
     <string name="communal_widget_picker_description" msgid="490515450110487871">"همه می‌توانند ابزاره‌ها را در صفحه قفل شما ببینند، حتی اگر رایانه لوحی قفل باشد."</string>
     <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"لغو انتخاب ابزاره"</string>
+    <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
+    <skip />
+    <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
+    <skip />
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"ابزاره‌های صفحه قفل"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"برای باز کردن برنامه بااستفاده از ابزاره، باید هویت خودتان را به‌تأیید برسانید. همچنین، به‌خاطر داشته باشید که همه می‌توانند آن‌ها را مشاهده کنند، حتی وقتی رایانه لوحی‌تان قفل است. برخی‌از ابزاره‌ها ممکن است برای صفحه قفل درنظر گرفته نشده باشند و ممکن است اضافه کردن آن‌ها در اینجا ناامن باشد."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"متوجه‌ام"</string>
@@ -571,6 +581,10 @@
     <string name="clear_all_notifications_text" msgid="348312370303046130">"پاک کردن همه موارد"</string>
     <string name="manage_notifications_text" msgid="6885645344647733116">"مدیریت"</string>
     <string name="manage_notifications_history_text" msgid="57055985396576230">"سابقه"</string>
+    <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
+    <skip />
+    <!-- no translation found for notification_history_button_description (1578657591405033383) -->
+    <skip />
     <string name="notification_section_header_incoming" msgid="850925217908095197">"جدید"</string>
     <string name="notification_section_header_gentle" msgid="6804099527336337197">"بی‌صدا"</string>
     <string name="notification_section_header_alerting" msgid="5581175033680477651">"اعلان‌ها"</string>
@@ -810,7 +824,7 @@
     <string name="keyboard_key_page_up" msgid="173914303254199845">"صفحه بعد"</string>
     <string name="keyboard_key_page_down" msgid="9035902490071829731">"صفحه قبل"</string>
     <string name="keyboard_key_forward_del" msgid="5325501825762733459">"حذف"</string>
-    <string name="keyboard_key_esc" msgid="6230365950511411322">"کلید گریز"</string>
+    <string name="keyboard_key_esc" msgid="6230365950511411322">"Esc"</string>
     <string name="keyboard_key_move_home" msgid="3496502501803911971">"ابتدا"</string>
     <string name="keyboard_key_move_end" msgid="99190401463834854">"End"</string>
     <string name="keyboard_key_insert" msgid="4621692715704410493">"Insert"</string>
@@ -1405,8 +1419,7 @@
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"نماد ازهم بازکردن"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"یا"</string>
     <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"دستگیره کشاندن"</string>
-    <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) -->
-    <skip />
+    <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"تنظیمات صفحه‌کلید"</string>
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"پیمایش کردن بااستفاده از صفحه‌کلید"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"آشنایی با میان‌برهای صفحه‌کلید"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"پیمایش کردن بااستفاده از صفحه لمسی"</string>
@@ -1464,8 +1477,6 @@
     <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"ارائه‌شده از برنامه‌ها"</string>
     <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"نمایشگر"</string>
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"نامشخص"</string>
-    <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) -->
-    <skip />
-    <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) -->
-    <skip />
+    <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"بازنشانی کردن کاشی‌ها"</string>
+    <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"اندازه و ترتیب کاشی‌ها به حالت اولیه‌شان بازنشانی شود؟"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml
index ca6a97b..a40cbae 100644
--- a/packages/SystemUI/res/values-fi/strings.xml
+++ b/packages/SystemUI/res/values-fi/strings.xml
@@ -136,11 +136,17 @@
     <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Laite, jonka sisältöä tallennat: <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Lopeta tallennus"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Näyttöä jaetaan"</string>
+    <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
+    <skip />
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Lopetetaanko näytön jakaminen?"</string>
+    <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
+    <skip />
     <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Jaat tällä hetkellä koko näyttöä: <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>"</string>
     <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Jaat tällä hetkellä koko näyttöä sovellukselle"</string>
     <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"Jaat tällä hetkellä tätä: <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>"</string>
     <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"Jaat tällä hetkellä sovellusta"</string>
+    <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
+    <skip />
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Lopeta jakaminen"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Näyttöä striimataan"</string>
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Lopetetaanko striimaus?"</string>
@@ -515,6 +521,10 @@
     <string name="communal_widget_picker_title" msgid="1953369090475731663">"Lukitusnäytön widgetit"</string>
     <string name="communal_widget_picker_description" msgid="490515450110487871">"Kaikki voivat nähdä widgetit lukitusnäytöllä, vaikka tabletti olisi lukittuna."</string>
     <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"poista widgetin valinta"</string>
+    <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
+    <skip />
+    <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
+    <skip />
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Lukitusnäytön widgetit"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Jos haluat avata sovelluksen käyttämällä widgetiä, sinun täytyy vahvistaa henkilöllisyytesi. Muista myös, että widgetit näkyvät kaikille, vaikka tabletti olisi lukittuna. Jotkin widgetit on ehkä tarkoitettu lukitusnäytölle, ja niiden lisääminen tänne ei välttämättä ole turvallista."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Selvä"</string>
@@ -571,6 +581,10 @@
     <string name="clear_all_notifications_text" msgid="348312370303046130">"Tyhjennä kaikki"</string>
     <string name="manage_notifications_text" msgid="6885645344647733116">"Muuta asetuksia"</string>
     <string name="manage_notifications_history_text" msgid="57055985396576230">"Historia"</string>
+    <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
+    <skip />
+    <!-- no translation found for notification_history_button_description (1578657591405033383) -->
+    <skip />
     <string name="notification_section_header_incoming" msgid="850925217908095197">"Uudet"</string>
     <string name="notification_section_header_gentle" msgid="6804099527336337197">"Äänetön"</string>
     <string name="notification_section_header_alerting" msgid="5581175033680477651">"Ilmoitukset"</string>
@@ -849,7 +863,7 @@
     <string name="group_system_access_all_apps_search" msgid="1553588630154197469">"Avaa sovelluslista"</string>
     <string name="group_system_access_system_settings" msgid="8731721963449070017">"Avaa asetukset"</string>
     <string name="group_system_access_google_assistant" msgid="7210074957915968110">"Avaa Assistant"</string>
-    <string name="group_system_lock_screen" msgid="7391191300363416543">"Lukitusnäyttö"</string>
+    <string name="group_system_lock_screen" msgid="7391191300363416543">"Lukitse näyttö"</string>
     <string name="group_system_quick_memo" msgid="3764560265935722903">"Tee muistiinpano"</string>
     <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Multitaskaus"</string>
     <string name="system_multitasking_rhs" msgid="8714224917276297810">"Käytä jaettua näyttöä niin, että nyt käytettävä sovellus on oikealla"</string>
@@ -1405,8 +1419,7 @@
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Laajennuskuvake"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"tai"</string>
     <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Vetokahva"</string>
-    <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) -->
-    <skip />
+    <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Näppäimistön asetukset"</string>
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Siirry käyttämällä näppäimistöä"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Opettele pikanäppäimiä"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Siirry käyttämällä kosketuslevyä"</string>
@@ -1464,8 +1477,6 @@
     <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Sovellusten tarjoama"</string>
     <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Näyttö"</string>
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Tuntematon"</string>
-    <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) -->
-    <skip />
-    <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) -->
-    <skip />
+    <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"Palauta laatat"</string>
+    <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"Palautetaanko laatat alkuperäiseen järjestykseen ja kokoon?"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml
index 4496b5e..288be34 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings.xml
@@ -136,11 +136,17 @@
     <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Vous êtes en train d\'enregistrer <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Arrêter l\'enregistrement"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Partage d\'écran en cours…"</string>
+    <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
+    <skip />
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Arrêter le partage d\'écran?"</string>
+    <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
+    <skip />
     <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Vous partagez actuellement l\'intégralité de votre écran avec <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>"</string>
     <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Vous partagez actuellement l\'intégralité de votre écran avec une appli"</string>
     <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"Vous partagez actuellement <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>"</string>
     <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"Vous partagez actuellement une appli"</string>
+    <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
+    <skip />
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Arrêter le partage"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Diffusion de l\'écran en cours…"</string>
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Arrêter la diffusion?"</string>
@@ -515,6 +521,10 @@
     <string name="communal_widget_picker_title" msgid="1953369090475731663">"Widgets de l\'écran de verrouillage"</string>
     <string name="communal_widget_picker_description" msgid="490515450110487871">"N\'importe qui peut voir les widgets sur votre écran de verrouillage, même si votre tablette est verrouillée."</string>
     <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"désélectionner le widget"</string>
+    <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
+    <skip />
+    <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
+    <skip />
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Widgets de l\'écran de verrouillage"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Pour ouvrir une appli à l\'aide d\'un widget, vous devrez confirmer votre identité. En outre, gardez à l\'esprit que tout le monde peut voir les widgets, même lorsque votre tablette est verrouillée. Certains widgets n\'ont peut-être pas été conçus pour votre écran de verrouillage, et il pourrait être dangereux de les ajouter ici."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"OK"</string>
@@ -554,7 +564,7 @@
     <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Diffuser une appli"</string>
     <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"Diffuser l\'intégralité de l\'écran"</string>
     <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"Lorsque vous diffusez l\'intégralité de votre écran, tout ce qui s\'y trouve est visible. Par conséquent, soyez prudent avec les mots de passe, les détails du paiement, les messages, les photos et les contenus audio et vidéo."</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"Lorsque vous diffusez une appli, tout ce qui s\'y affiche ou s\'y joue est visible. Par conséquent, soyez prudent avec les mots de passe, les détails du paiement, les messages, les photos et les contenus audio et vidéo."</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"Lorsque vous diffusez une appli, tout ce qui s\'y affiche ou s\'y joue est visible. Par conséquent, soyez prudent avec les mots de passe, les détails du mode de paiement, les messages, les photos et les contenus audio et vidéo."</string>
     <string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"Diffuser l\'écran"</string>
     <string name="media_projection_entry_cast_app_selector_title" msgid="6323062146661922387">"Choisir l\'appli à diffuser"</string>
     <string name="media_projection_entry_generic_permission_dialog_title" msgid="4519802931547483628">"Commencer à partager?"</string>
@@ -571,6 +581,10 @@
     <string name="clear_all_notifications_text" msgid="348312370303046130">"Tout effacer"</string>
     <string name="manage_notifications_text" msgid="6885645344647733116">"Gérer"</string>
     <string name="manage_notifications_history_text" msgid="57055985396576230">"Historique"</string>
+    <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
+    <skip />
+    <!-- no translation found for notification_history_button_description (1578657591405033383) -->
+    <skip />
     <string name="notification_section_header_incoming" msgid="850925217908095197">"Nouvelles"</string>
     <string name="notification_section_header_gentle" msgid="6804099527336337197">"Mode silencieux"</string>
     <string name="notification_section_header_alerting" msgid="5581175033680477651">"Notifications"</string>
@@ -849,12 +863,12 @@
     <string name="group_system_access_all_apps_search" msgid="1553588630154197469">"Ouvrir la liste des applis"</string>
     <string name="group_system_access_system_settings" msgid="8731721963449070017">"Ouvrir les paramètres"</string>
     <string name="group_system_access_google_assistant" msgid="7210074957915968110">"Ouvrir l\'Assistant"</string>
-    <string name="group_system_lock_screen" msgid="7391191300363416543">"Écran de verrouillage"</string>
+    <string name="group_system_lock_screen" msgid="7391191300363416543">"Verrouiller l\'écran"</string>
     <string name="group_system_quick_memo" msgid="3764560265935722903">"Prendre une note"</string>
     <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Multitâche"</string>
     <string name="system_multitasking_rhs" msgid="8714224917276297810">"Utiliser l\'Écran divisé avec l\'appli actuelle à droite"</string>
     <string name="system_multitasking_lhs" msgid="8402954791206308783">"Utiliser l\'Écran divisé avec l\'appli actuelle à gauche"</string>
-    <string name="system_multitasking_full_screen" msgid="336048080383640562">"Passer de l\'écran divisé au plein écran"</string>
+    <string name="system_multitasking_full_screen" msgid="336048080383640562">"Passer de l\'Écran divisé au plein écran"</string>
     <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Passer à l\'appli à droite ou en dessous avec l\'Écran divisé"</string>
     <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Passer à l\'appli à gauche ou au-dessus avec l\'Écran divisé"</string>
     <string name="system_multitasking_replace" msgid="7410071959803642125">"En mode d\'écran divisé : remplacer une appli par une autre"</string>
@@ -1245,7 +1259,7 @@
     <string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"Un problème est survenu lors de la lecture du niveau de charge de la pile"</string>
     <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Touchez pour en savoir plus"</string>
     <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Aucune alarme définie"</string>
-    <string name="accessibility_bouncer" msgid="5896923685673320070">"entrer verrouillage de l\'écran"</string>
+    <string name="accessibility_bouncer" msgid="5896923685673320070">"accéder au verrouillage de l\'écran"</string>
     <string name="accessibility_side_fingerprint_indicator_label" msgid="1673807833352363712">"Toucher le capteur d\'empreintes digitales. Il s\'agit du bouton le plus court sur le côté du téléphone"</string>
     <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Capteur d\'empreintes digitales"</string>
     <string name="accessibility_authenticate_hint" msgid="798914151813205721">"authentifier"</string>
@@ -1405,8 +1419,7 @@
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Icône Développer"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ou"</string>
     <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Poignée de déplacement"</string>
-    <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) -->
-    <skip />
+    <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Paramètres du clavier"</string>
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Naviguer à l\'aide de votre clavier"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Apprenez à utiliser les raccourcis-clavier"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Naviguer à l\'aide de votre pavé tactile"</string>
@@ -1464,8 +1477,6 @@
     <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Fournies par des applis"</string>
     <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Affichage"</string>
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Inconnu"</string>
-    <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) -->
-    <skip />
-    <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) -->
-    <skip />
+    <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"Réinitialiser les tuiles"</string>
+    <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"Réinitialiser les tuiles à leur ordre et à leur taille par défaut?"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index 77b98eb..9d9f3f9 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -136,11 +136,17 @@
     <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Vous enregistrez actuellement <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Arrêter l\'enregistrement"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Partage de l\'écran…"</string>
+    <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
+    <skip />
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Arrêter le partage d\'écran ?"</string>
+    <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
+    <skip />
     <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Vous partagez actuellement l\'intégralité de votre écran avec <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>"</string>
     <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Vous partagez actuellement l\'intégralité de votre écran avec une appli"</string>
     <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"Vous partagez actuellement <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>"</string>
     <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"Vous partagez actuellement une appli"</string>
+    <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
+    <skip />
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Arrêter le partage"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Cast de l\'écran"</string>
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Arrêter de caster ?"</string>
@@ -322,11 +328,11 @@
     <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Entrée"</string>
     <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Appareils auditifs"</string>
     <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Activation…"</string>
-    <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Rotation automatique"</string>
+    <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Rotation auto"</string>
     <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Rotation automatique de l\'écran"</string>
     <string name="quick_settings_location_label" msgid="2621868789013389163">"Localisation"</string>
-    <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"Économiseur d\'écran"</string>
-    <string name="quick_settings_camera_label" msgid="5612076679385269339">"Accès à l\'appareil photo"</string>
+    <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"Économ. d\'écran"</string>
+    <string name="quick_settings_camera_label" msgid="5612076679385269339">"Accès caméra"</string>
     <string name="quick_settings_mic_label" msgid="8392773746295266375">"Accès au micro"</string>
     <string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"Disponible"</string>
     <string name="quick_settings_camera_mic_blocked" msgid="4710884905006788281">"Bloqué"</string>
@@ -368,7 +374,7 @@
     <string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"Avertissement : <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
     <string name="quick_settings_work_mode_label" msgid="6440531507319809121">"Applis pro"</string>
     <string name="quick_settings_work_mode_paused_state" msgid="6681788236383735976">"En pause"</string>
-    <string name="quick_settings_night_display_label" msgid="8180030659141778180">"Éclairage nocturne"</string>
+    <string name="quick_settings_night_display_label" msgid="8180030659141778180">"Éclair. nocturne"</string>
     <string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"Activé la nuit"</string>
     <string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"Jusqu\'à l\'aube"</string>
     <string name="quick_settings_night_secondary_label_on_at" msgid="3584738542293528235">"À partir de <xliff:g id="TIME">%s</xliff:g>"</string>
@@ -515,6 +521,10 @@
     <string name="communal_widget_picker_title" msgid="1953369090475731663">"Widgets sur l\'écran de verrouillage"</string>
     <string name="communal_widget_picker_description" msgid="490515450110487871">"N\'importe qui peut consulter les widgets sur votre écran de verrouillage, même si votre tablette est verrouillée."</string>
     <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"désélectionner le widget"</string>
+    <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
+    <skip />
+    <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
+    <skip />
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Widgets pour l\'écran de verrouillage"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Pour ouvrir une appli à l\'aide d\'un widget, vous devez confirmer qu\'il s\'agit bien de vous. N\'oubliez pas non plus que tout le monde peut voir vos widgets, même lorsque votre tablette est verrouillée. Certains d\'entre eux n\'ont pas été conçus pour l\'écran de verrouillage et les ajouter à cet endroit peut s\'avérer dangereux."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"OK"</string>
@@ -571,6 +581,10 @@
     <string name="clear_all_notifications_text" msgid="348312370303046130">"Tout effacer"</string>
     <string name="manage_notifications_text" msgid="6885645344647733116">"Gérer"</string>
     <string name="manage_notifications_history_text" msgid="57055985396576230">"Historique"</string>
+    <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
+    <skip />
+    <!-- no translation found for notification_history_button_description (1578657591405033383) -->
+    <skip />
     <string name="notification_section_header_incoming" msgid="850925217908095197">"Nouvelles notifications"</string>
     <string name="notification_section_header_gentle" msgid="6804099527336337197">"Silencieux"</string>
     <string name="notification_section_header_alerting" msgid="5581175033680477651">"Notifications"</string>
@@ -1405,8 +1419,7 @@
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Icône Développer"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ou"</string>
     <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Poignée de déplacement"</string>
-    <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) -->
-    <skip />
+    <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Paramètres du clavier"</string>
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Naviguer à l\'aide du clavier"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Découvrir les raccourcis clavier"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Naviguer à l\'aide de votre pavé tactile"</string>
@@ -1438,7 +1451,7 @@
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Contrôle de la maison"</string>
     <string name="home_controls_dream_description" msgid="4644150952104035789">"Domotique sous forme d\'économiseur d\'écran"</string>
     <string name="volume_undo_action" msgid="5815519725211877114">"Annuler"</string>
-    <string name="back_edu_toast_content" msgid="4530314597378982956">"Pour retourner en arrière, glissez vers la gauche ou la droite avec trois doigts sur le pavé tactile"</string>
+    <string name="back_edu_toast_content" msgid="4530314597378982956">"Pour retourner en arrière, balayez vers la gauche ou la droite avec trois doigts sur le pavé tactile"</string>
     <string name="home_edu_toast_content" msgid="3381071147871955415">"Pour revenir à l\'accueil, balayez vers le haut avec trois doigts sur le pavé tactile"</string>
     <string name="overview_edu_toast_content" msgid="5797030644017804518">"Pour afficher les applis récentes, balayez vers le haut avec trois doigts sur le pavé tactile et maintenez-les."</string>
     <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"Pour afficher toutes vos applis, appuyez sur la touche d\'action de votre clavier"</string>
@@ -1464,8 +1477,6 @@
     <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Fournis par des applis"</string>
     <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Écran"</string>
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Inconnu"</string>
-    <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) -->
-    <skip />
-    <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) -->
-    <skip />
+    <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"Réinitialiser les blocs"</string>
+    <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"Rétablir l\'ordre et la taille d\'origine des blocs ?"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml
index 3b94095..1430bc2 100644
--- a/packages/SystemUI/res/values-gl/strings.xml
+++ b/packages/SystemUI/res/values-gl/strings.xml
@@ -136,11 +136,17 @@
     <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Estás gravando <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Deter gravación"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Compartindo pantalla"</string>
+    <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
+    <skip />
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Queres deixar de compartir a pantalla?"</string>
+    <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
+    <skip />
     <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Estás compartindo toda a pantalla con <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>"</string>
     <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Estás compartindo toda a pantalla cunha aplicación"</string>
     <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"Estás compartindo <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>"</string>
     <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"Estás compartindo unha aplicación"</string>
+    <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
+    <skip />
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Deixar de compartir"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Emitindo pantalla"</string>
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Queres deter a emisión?"</string>
@@ -515,6 +521,10 @@
     <string name="communal_widget_picker_title" msgid="1953369090475731663">"Widgets da pantalla de bloqueo"</string>
     <string name="communal_widget_picker_description" msgid="490515450110487871">"Calquera pode ver os widgets na pantalla de bloqueo, mesmo coa tableta bloqueada"</string>
     <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"anular a selección do widget"</string>
+    <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
+    <skip />
+    <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
+    <skip />
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Widgets da pantalla de bloqueo"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Para abrir unha aplicación mediante un widget, tes que verificar a túa identidade. Ten en conta que pode velos calquera persoa, mesmo coa tableta bloqueada. Pode ser que algúns widgets non estean pensados para a túa pantalla de bloqueo, polo que talvez non sexa seguro engadilos aquí."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Entendido"</string>
@@ -571,6 +581,10 @@
     <string name="clear_all_notifications_text" msgid="348312370303046130">"Eliminar todo"</string>
     <string name="manage_notifications_text" msgid="6885645344647733116">"Xestionar"</string>
     <string name="manage_notifications_history_text" msgid="57055985396576230">"Historial"</string>
+    <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
+    <skip />
+    <!-- no translation found for notification_history_button_description (1578657591405033383) -->
+    <skip />
     <string name="notification_section_header_incoming" msgid="850925217908095197">"Notificacións novas"</string>
     <string name="notification_section_header_gentle" msgid="6804099527336337197">"Silenciadas"</string>
     <string name="notification_section_header_alerting" msgid="5581175033680477651">"Notificacións"</string>
@@ -1405,8 +1419,7 @@
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Icona de despregar"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ou"</string>
     <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Controlador de arrastre"</string>
-    <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) -->
-    <skip />
+    <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Configuración do teclado"</string>
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Navega co teclado"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Aprende a usar os atallos de teclado"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Navega co panel táctil"</string>
@@ -1464,8 +1477,6 @@
     <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Provenientes de aplicacións"</string>
     <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Visualización"</string>
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Categoría descoñecida"</string>
-    <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) -->
-    <skip />
-    <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) -->
-    <skip />
+    <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"Restablecer as tarxetas"</string>
+    <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"Queres restablecer as tarxetas ao seu tamaño e orde orixinais?"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml
index f87184a..bc2739f 100644
--- a/packages/SystemUI/res/values-gu/strings.xml
+++ b/packages/SystemUI/res/values-gu/strings.xml
@@ -136,11 +136,17 @@
     <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"તમે હાલમાં <xliff:g id="APP_NAME">%1$s</xliff:g> રેકોર્ડ કરી રહ્યાં છો"</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"રેકોર્ડિંગ રોકો"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"સ્ક્રીન શેર કરી રહ્યાં છીએ"</string>
+    <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
+    <skip />
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"સ્ક્રીન શેર કરવાનું રોકીએ?"</string>
+    <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
+    <skip />
     <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"તમે હાલમાં <xliff:g id="HOST_APP_NAME">%1$s</xliff:g> વડે તમારી પૂર્ણ સ્ક્રીન શેર કરી રહ્યાં છો"</string>
     <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"તમે હાલમાં ઍપ વડે તમારી પૂર્ણ સ્ક્રીન શેર કરી રહ્યાં છો"</string>
     <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"તમે હાલમાં <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> શેર કરી રહ્યાં છો"</string>
     <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"તમે હાલમાં ઍપ શેર કરી રહ્યાં છો"</string>
+    <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
+    <skip />
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"શેર કરવાનું રોકો"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"સ્ક્રીન કાસ્ટ કરી રહ્યાં છીએ"</string>
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"કાસ્ટ કરવાનું રોકીએ?"</string>
@@ -515,6 +521,10 @@
     <string name="communal_widget_picker_title" msgid="1953369090475731663">"લૉક સ્ક્રીન વિજેટ"</string>
     <string name="communal_widget_picker_description" msgid="490515450110487871">"તમારું ટૅબ્લેટ લૉક કરેલું હોય તો પણ કોઈપણ તમારી લૉક સ્ક્રીન પર વિજેટ જોઈ શકે છે."</string>
     <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"વિજેટ નાપસંદ કરો"</string>
+    <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
+    <skip />
+    <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
+    <skip />
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"લૉક સ્ક્રીન વિજેટ"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"વિજેટનો ઉપયોગ કરીને ઍપ ખોલવા માટે, તમારે એ ચકાસણી કરવાની જરૂર રહેશે કે આ તમે જ છો. તે ઉપરાંત, ધ્યાનમાં રાખો કે તમારું ટૅબ્લેટ લૉક કરેલું હોય તો પણ કોઈપણ વ્યક્તિ તેમને જોઈ શકે છે. અમુક વિજેટ કદાચ તમારી લૉક સ્ક્રીન માટે બનાવવામાં આવ્યા ન હોઈ શકે છે અને તેમને અહીં ઉમેરવાનું અસલામત હોઈ શકે છે."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"સમજાઈ ગયું"</string>
@@ -571,6 +581,10 @@
     <string name="clear_all_notifications_text" msgid="348312370303046130">"બધુ સાફ કરો"</string>
     <string name="manage_notifications_text" msgid="6885645344647733116">"મેનેજ કરો"</string>
     <string name="manage_notifications_history_text" msgid="57055985396576230">"ઇતિહાસ"</string>
+    <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
+    <skip />
+    <!-- no translation found for notification_history_button_description (1578657591405033383) -->
+    <skip />
     <string name="notification_section_header_incoming" msgid="850925217908095197">"નવા"</string>
     <string name="notification_section_header_gentle" msgid="6804099527336337197">"સાઇલન્ટ"</string>
     <string name="notification_section_header_alerting" msgid="5581175033680477651">"નોટિફિકેશન"</string>
@@ -866,7 +880,7 @@
     <string name="keyboard_shortcut_group_applications" msgid="7386239431100651266">"ઍપ્લિકેશનો"</string>
     <string name="keyboard_shortcut_group_applications_assist" msgid="6772492350416591448">"Assistant"</string>
     <string name="keyboard_shortcut_group_applications_browser" msgid="2776211137869809251">"બ્રાઉઝર"</string>
-    <string name="keyboard_shortcut_group_applications_contacts" msgid="2807268086386201060">"સંપર્કો"</string>
+    <string name="keyboard_shortcut_group_applications_contacts" msgid="2807268086386201060">"Contacts"</string>
     <string name="keyboard_shortcut_group_applications_email" msgid="7852376788894975192">"ઇમેઇલ"</string>
     <string name="keyboard_shortcut_group_applications_sms" msgid="6912633831752843566">"SMS"</string>
     <string name="keyboard_shortcut_group_applications_music" msgid="9032078456666204025">"મ્યુઝિક"</string>
@@ -1405,8 +1419,7 @@
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"\'મોટું કરો\'નું આઇકન"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"અથવા"</string>
     <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"ઑબ્જેક્ટ ખેંચવાનું હૅન્ડલ"</string>
-    <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) -->
-    <skip />
+    <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"કીબોર્ડના સેટિંગ"</string>
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"તમારા કીબોર્ડ વડે નૅવિગેટ કરો"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"કીબોર્ડ શૉર્ટકર્ટ જાણો"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"તમારા ટચપૅડ વડે નૅવિગેટ કરો"</string>
@@ -1464,8 +1477,6 @@
     <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"ઍપ દ્વારા પ્રદાન કરવામાં આવેલી"</string>
     <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"ડિસ્પ્લે"</string>
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"અજાણ"</string>
-    <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) -->
-    <skip />
-    <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) -->
-    <skip />
+    <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"ટાઇલને રીસેટ કરો"</string>
+    <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"ટાઇલને તેમના મૂળ ક્રમ અને કદમાં રીસેટ કરીએ?"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml
index 767f9df..f9a1fa5 100644
--- a/packages/SystemUI/res/values-hi/strings.xml
+++ b/packages/SystemUI/res/values-hi/strings.xml
@@ -136,11 +136,17 @@
     <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"फ़िलहाल, <xliff:g id="APP_NAME">%1$s</xliff:g> की रिकॉर्डिंग की जा रही है"</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"रिकॉर्ड करना बंद करें"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"स्क्रीन शेयर की जा रही है"</string>
+    <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
+    <skip />
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"स्क्रीन शेयर करना बंद करना है?"</string>
+    <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
+    <skip />
     <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"फ़िलहाल, <xliff:g id="HOST_APP_NAME">%1$s</xliff:g> पर पूरी स्क्रीन शेयर की जा रही है"</string>
     <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"फ़िलहाल, किसी ऐप्लिकेशन पर पूरी स्क्रीन शेयर की जा रही है"</string>
     <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"फ़िलहाल, <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> शेयर किया जा रहा है"</string>
     <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"फ़िलहाल, कोई ऐप्लिकेशन शेयर किया जा रहा है"</string>
+    <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
+    <skip />
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"शेयर करना बंद करें"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"स्क्रीन कास्ट की जा रही है"</string>
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"कास्ट करना बंद करना है?"</string>
@@ -515,6 +521,10 @@
     <string name="communal_widget_picker_title" msgid="1953369090475731663">"लॉक स्क्रीन विजेट"</string>
     <string name="communal_widget_picker_description" msgid="490515450110487871">"टैबलेट लॉक होने के बावजूद, कोई भी व्यक्ति इसकी लॉक स्क्रीन पर विजेट देख सकता है."</string>
     <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"विजेट से चुने हुए का निशान हटाएं"</string>
+    <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
+    <skip />
+    <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
+    <skip />
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"लॉक स्क्रीन विजेट"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"किसी विजेट से कोई ऐप्लिकेशन खोलने के लिए, आपको अपनी पहचान की पुष्टि करनी होगी. ध्यान रखें कि आपके टैबलेट के लॉक होने पर भी, कोई व्यक्ति विजेट देख सकता है. ऐसा हो सकता है कि कुछ विजेट, लॉक स्क्रीन पर दिखाने के लिए न बने हों. इन्हें लॉक स्क्रीन पर जोड़ना असुरक्षित हो सकता है."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"ठीक है"</string>
@@ -553,8 +563,8 @@
     <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"क्या स्क्रीन को कास्ट करना है?"</string>
     <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"एक ऐप्लिकेशन को कास्ट करें"</string>
     <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"पूरी स्क्रीन को कास्ट करें"</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"पूरी स्क्रीन को कास्ट करने के दौरान, उस पर दिख रहा कॉन्टेंट दूसरी स्क्रीन पर भी दिखता है. इसलिए, कास्ट करते समय पासवर्ड, पेमेंट के तरीके की जानकारी, मैसेज, फ़ोटो, ऑडियो, और वीडियो को लेकर सावधानी बरतें."</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"किसी ऐप्लिकेशन को कास्ट करने के दौरान, उस पर दिख रहा कॉन्टेंट या चल रहा मीडिया दूसरी स्क्रीन पर भी दिखता है. इसलिए, कास्ट करते समय पासवर्ड, पेमेंट के तरीके की जानकारी, मैसेज, फ़ोटो, ऑडियो, और वीडियो को लेकर सावधानी बरतें."</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"पूरी स्क्रीन को कास्ट करने पर, उस पर दिख रहा कॉन्टेंट दूसरी स्क्रीन पर भी दिखता है. इसलिए, पासवर्ड, पेमेंट के तरीके की जानकारी, मैसेज, फ़ोटो, ऑडियो, और वीडियो को लेकर सावधानी बरतें."</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"किसी ऐप्लिकेशन को कास्ट करने पर, उस पर दिख रहा कॉन्टेंट या चल रहा मीडिया दूसरी स्क्रीन पर भी दिखता है. इसलिए, पासवर्ड, पेमेंट के तरीके की जानकारी, मैसेज, फ़ोटो, ऑडियो, और वीडियो को लेकर सावधानी बरतें."</string>
     <string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"स्क्रीन कास्ट करें"</string>
     <string name="media_projection_entry_cast_app_selector_title" msgid="6323062146661922387">"कास्ट करने के लिए ऐप्लिकेशन चुनें"</string>
     <string name="media_projection_entry_generic_permission_dialog_title" msgid="4519802931547483628">"क्या मीडिया शेयर करना है?"</string>
@@ -571,6 +581,10 @@
     <string name="clear_all_notifications_text" msgid="348312370303046130">"सभी हटाएं"</string>
     <string name="manage_notifications_text" msgid="6885645344647733116">"मैनेज करें"</string>
     <string name="manage_notifications_history_text" msgid="57055985396576230">"इतिहास"</string>
+    <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
+    <skip />
+    <!-- no translation found for notification_history_button_description (1578657591405033383) -->
+    <skip />
     <string name="notification_section_header_incoming" msgid="850925217908095197">"नई सूचनाएं"</string>
     <string name="notification_section_header_gentle" msgid="6804099527336337197">"साइलेंट मोड में मिली सूचनाएं"</string>
     <string name="notification_section_header_alerting" msgid="5581175033680477651">"सूचनाएं"</string>
@@ -848,12 +862,12 @@
     <string name="group_system_cycle_back" msgid="8194102916946802902">"हाल ही में इस्तेमाल किए गए ऐप्लिकेशन के पिछले पेज पर जाने के लिए"</string>
     <string name="group_system_access_all_apps_search" msgid="1553588630154197469">"ऐप्लिकेशन की सूची खोलने के लिए"</string>
     <string name="group_system_access_system_settings" msgid="8731721963449070017">"सेटिंग खोलने के लिए"</string>
-    <string name="group_system_access_google_assistant" msgid="7210074957915968110">"Google Assistant खोलने के लिए"</string>
+    <string name="group_system_access_google_assistant" msgid="7210074957915968110">"सहायक ऐप्लिकेशन खोलने के लिए"</string>
     <string name="group_system_lock_screen" msgid="7391191300363416543">"स्क्रीन लॉक करने के लिए"</string>
-    <string name="group_system_quick_memo" msgid="3764560265935722903">"नोट करने के लिए"</string>
+    <string name="group_system_quick_memo" msgid="3764560265935722903">"नोट बनाने के लिए"</string>
     <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"मल्टीटास्किंग (एक साथ कई काम करना)"</string>
-    <string name="system_multitasking_rhs" msgid="8714224917276297810">"मौजूदा ऐप्लिकेशन को दाईं ओर दिखाने वाली स्प्लिट स्क्रीन इस्तेमाल करें"</string>
-    <string name="system_multitasking_lhs" msgid="8402954791206308783">"मौजूदा ऐप्लिकेशन को बाईं ओर दिखाने वाली स्प्लिट स्क्रीन इस्तेमाल करें"</string>
+    <string name="system_multitasking_rhs" msgid="8714224917276297810">"स्प्लिट स्क्रीन में मौजूदा ऐप्लिकेशन को दाईं ओर दिखाने के लिए"</string>
+    <string name="system_multitasking_lhs" msgid="8402954791206308783">"स्प्लिट स्क्रीन में मौजूदा ऐप्लिकेशन को बाईं ओर दिखाने के लिए"</string>
     <string name="system_multitasking_full_screen" msgid="336048080383640562">"स्प्लिट स्क्रीन से फ़ुल स्क्रीन मोड पर स्विच करने के लिए"</string>
     <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"स्प्लिट स्क्रीन पर, दाईं ओर या नीचे के ऐप पर स्विच करने के लिए"</string>
     <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"स्प्लिट स्क्रीन पर, बाईं ओर या ऊपर के ऐप पर स्विच करने के लिए"</string>
@@ -1405,12 +1419,11 @@
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"बड़ा करने का आइकॉन"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"या"</string>
     <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"खींचकर छोड़ने वाला हैंडल"</string>
-    <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) -->
-    <skip />
+    <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"कीबोर्ड सेटिंग"</string>
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"कीबोर्ड का इस्तेमाल करके नेविगेट करें"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"कीबोर्ड शॉर्टकट के बारे में जानें"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"टचपैड का इस्तेमाल करके नेविगेट करें"</string>
-    <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"टचपैड पर हाथ के जेस्चर के बारे में जानें"</string>
+    <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"टचपैड से जुड़े जेस्चर के बारे में जानें"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"कीबोर्ड और टचपैड का इस्तेमाल करके नेविगेट करें"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"टचपैड पर हाथ के जेस्चर, कीबोर्ड शॉर्टकट वगैरह के बारे में जानें"</string>
     <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"वापस जाएं"</string>
@@ -1446,7 +1459,7 @@
     <string name="redacted_notification_single_line_text" msgid="8684166405005242945">"देखने के लिए डिवाइस अनलॉक करें"</string>
     <string name="contextual_education_dialog_title" msgid="4630392552837487324">"कॉन्टेक्स्ट के हिसाब से जानकारी"</string>
     <string name="back_edu_notification_title" msgid="5624780717751357278">"वापस जाने के लिए, अपने डिवाइस के टचपैड का इस्तेमाल करें"</string>
-    <string name="back_edu_notification_content" msgid="2497557451540954068">"तीन उंगलियों से बाईं या दाईं ओर स्वाइप करें. जेस्चर के बारे में ज़्यादा जानने के लिए टैप करें."</string>
+    <string name="back_edu_notification_content" msgid="2497557451540954068">"तीन उंगलियों से बाईं या दाईं ओर स्वाइप करें. ज़्यादा जेस्चर के बारे में जानने के लिए टैप करें."</string>
     <string name="home_edu_notification_title" msgid="6097902076909654045">"होम पर जाने के लिए, अपने डिवाइस के टचपैड का इस्तेमाल करें"</string>
     <string name="home_edu_notification_content" msgid="6631697734535766588">"तीन उंगलियों से ऊपर की ओर स्वाइप करें. जेस्चर के बारे में ज़्यादा जानने के लिए टैप करें."</string>
     <string name="overview_edu_notification_title" msgid="1265824157319562406">"हाल ही में इस्तेमाल हुए ऐप्लिकेशन देखने के लिए, अपने डिवाइस के टचपैड का इस्तेमाल करें"</string>
@@ -1464,8 +1477,6 @@
     <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"ऐप्लिकेशन से मिली जानकारी"</string>
     <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"डिसप्ले"</string>
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"कोई जानकारी नहीं है"</string>
-    <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) -->
-    <skip />
-    <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) -->
-    <skip />
+    <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"टाइल रीसेट करें"</string>
+    <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"क्या आपको टाइल, उनके ओरिजनल क्रम और साइज़ पर रीसेट करने हैं?"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml
index f040451..4a17fdc 100644
--- a/packages/SystemUI/res/values-hr/strings.xml
+++ b/packages/SystemUI/res/values-hr/strings.xml
@@ -136,11 +136,17 @@
     <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Trenutačno snimate aplikaciju <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Zaustavi snimanje"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Dijeljenje zaslona"</string>
+    <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
+    <skip />
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Želite li zaustaviti dijeljenje zaslona?"</string>
+    <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
+    <skip />
     <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Trenutačno dijelite cijeli zaslon s aplikacijom <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>"</string>
     <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Trenutačno dijelite cijeli zaslon s aplikacijom"</string>
     <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"Trenutačno dijelite aplikaciju <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>"</string>
     <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"Trenutačno dijelite aplikaciju"</string>
+    <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
+    <skip />
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Zaustavi dijeljenje"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Emitiranje zaslona"</string>
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Želite li prestati emitirati?"</string>
@@ -515,6 +521,10 @@
     <string name="communal_widget_picker_title" msgid="1953369090475731663">"Widgeti zaključanog zaslona"</string>
     <string name="communal_widget_picker_description" msgid="490515450110487871">"Svi vide widgete na vašem zaključanom zaslonu, čak i ako je tablet zaključan."</string>
     <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"poništavanje odabira widgeta"</string>
+    <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
+    <skip />
+    <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
+    <skip />
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Widgeti na zaključanom zaslonu"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Da biste otvorili aplikaciju pomoću widgeta, trebate potvrditi da ste to vi. Također napominjemo da ih svatko može vidjeti, čak i ako je vaš tablet zaključan. Neki widgeti možda nisu namijenjeni za zaključani zaslon, pa ih možda nije sigurno dodati ovdje."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Shvaćam"</string>
@@ -555,7 +565,7 @@
     <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"Emitiranje cijelog zaslona"</string>
     <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"Kada emitirate cijeli zaslon, sve na zaslonu bit će vidljivo. Stoga pazite na stvari kao što su zaporke, podaci o plaćanju, poruke, fotografije te audio i videozapisi."</string>
     <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"Kada emitirate aplikaciju, sve što se prikazuje ili reproducira u toj aplikaciji bit će vidljivo. Stoga pazite na stvari kao što su zaporke, podaci o plaćanju, poruke, fotografije te audio i videozapisi."</string>
-    <string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"Emitiranje zaslona"</string>
+    <string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"Emitiraj zaslon"</string>
     <string name="media_projection_entry_cast_app_selector_title" msgid="6323062146661922387">"Odaberite aplikaciju za emitiranje"</string>
     <string name="media_projection_entry_generic_permission_dialog_title" msgid="4519802931547483628">"Želite li pokrenuti dijeljenje?"</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_entire_screen" msgid="5407906851409410209">"Kad dijelite, snimate ili emitirate, Android ima pristup svemu što je vidljivo na zaslonu ili se reproducira na uređaju. Stoga pazite na zaporke, podatke o plaćanju, poruke, fotografije te audio i videozapise."</string>
@@ -571,6 +581,10 @@
     <string name="clear_all_notifications_text" msgid="348312370303046130">"Izbriši sve"</string>
     <string name="manage_notifications_text" msgid="6885645344647733116">"Upravljajte"</string>
     <string name="manage_notifications_history_text" msgid="57055985396576230">"Povijest"</string>
+    <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
+    <skip />
+    <!-- no translation found for notification_history_button_description (1578657591405033383) -->
+    <skip />
     <string name="notification_section_header_incoming" msgid="850925217908095197">"Novo"</string>
     <string name="notification_section_header_gentle" msgid="6804099527336337197">"Bešumno"</string>
     <string name="notification_section_header_alerting" msgid="5581175033680477651">"Obavijesti"</string>
@@ -844,8 +858,8 @@
     <string name="group_system_go_back" msgid="2730322046244918816">"Natrag"</string>
     <string name="group_system_access_home_screen" msgid="4130366993484706483">"Otvaranje početnog zaslona"</string>
     <string name="group_system_overview_open_apps" msgid="5659958952937994104">"Prikaz nedavnih aplikacija"</string>
-    <string name="group_system_cycle_forward" msgid="5478663965957647805">"Kruženje unaprijed kroz nedavne aplikacije"</string>
-    <string name="group_system_cycle_back" msgid="8194102916946802902">"Kruženje unatrag kroz nedavne aplikacije"</string>
+    <string name="group_system_cycle_forward" msgid="5478663965957647805">"Listajte nedavne aplikacije (prema naprijed)"</string>
+    <string name="group_system_cycle_back" msgid="8194102916946802902">"Listajte nedavne aplikacije (unatrag)"</string>
     <string name="group_system_access_all_apps_search" msgid="1553588630154197469">"Otvaranje popisa aplikacija"</string>
     <string name="group_system_access_system_settings" msgid="8731721963449070017">"Otvaranje postavki"</string>
     <string name="group_system_access_google_assistant" msgid="7210074957915968110">"Otvaranje asistenta"</string>
@@ -1405,8 +1419,7 @@
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ikona za proširivanje"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ili"</string>
     <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Marker za povlačenje"</string>
-    <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) -->
-    <skip />
+    <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Postavke tipkovnice"</string>
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Krećite se pomoću tipkovnice"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Saznajte više o tipkovnim prečacima"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Krećite se pomoću dodirne podloge"</string>
@@ -1464,8 +1477,6 @@
     <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Pružaju aplikacije"</string>
     <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Prikaz"</string>
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Nepoznato"</string>
-    <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) -->
-    <skip />
-    <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) -->
-    <skip />
+    <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"Vraćanje kartica na zadano"</string>
+    <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"Želite li vratiti kartice na zadani redoslijed i veličine?"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml
index 629e2ff..ff5339f 100644
--- a/packages/SystemUI/res/values-hu/strings.xml
+++ b/packages/SystemUI/res/values-hu/strings.xml
@@ -136,11 +136,17 @@
     <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Jelenleg a következőről készít felvételt: <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Felvétel leállítása"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Képernyő megosztása…"</string>
+    <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
+    <skip />
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Leállítja a képernyőmegosztást?"</string>
+    <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
+    <skip />
     <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Jelenleg megosztja a teljes képernyőt a következővel: <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>"</string>
     <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Jelenleg megosztja a teljes képernyőt egy alkalmazással"</string>
     <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"Jelenleg megosztja a következőt: <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>"</string>
     <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"Jelenleg megoszt egy alkalmazást"</string>
+    <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
+    <skip />
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Megosztás leállítása"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Képernyőtartalom átküldése…"</string>
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Leállítja az átküldést?"</string>
@@ -515,6 +521,10 @@
     <string name="communal_widget_picker_title" msgid="1953369090475731663">"A lezárási képernyő moduljai"</string>
     <string name="communal_widget_picker_description" msgid="490515450110487871">"Bárki megtekintheti a modulokat a lezárási képernyőjén, még ha a táblagépe zárolva is van."</string>
     <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"modul kijelölésének megszüntetése"</string>
+    <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
+    <skip />
+    <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
+    <skip />
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"A lezárási képernyő moduljai"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Ha modul használatával szeretne megnyitni egy alkalmazást, igazolnia kell a személyazonosságát. Ne felejtse továbbá, hogy bárki megtekintheti a modulokat, még akkor is, amikor zárolva van a táblagép. Előfordulhat, hogy bizonyos modulokat nem a lezárási képernyőn való használatra terveztek, ezért nem biztonságos a hozzáadásuk."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Értem"</string>
@@ -571,6 +581,10 @@
     <string name="clear_all_notifications_text" msgid="348312370303046130">"Az összes törlése"</string>
     <string name="manage_notifications_text" msgid="6885645344647733116">"Kezelés"</string>
     <string name="manage_notifications_history_text" msgid="57055985396576230">"Előzmények"</string>
+    <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
+    <skip />
+    <!-- no translation found for notification_history_button_description (1578657591405033383) -->
+    <skip />
     <string name="notification_section_header_incoming" msgid="850925217908095197">"Új"</string>
     <string name="notification_section_header_gentle" msgid="6804099527336337197">"Néma"</string>
     <string name="notification_section_header_alerting" msgid="5581175033680477651">"Értesítések"</string>
@@ -1405,8 +1419,7 @@
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Kibontás ikon"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"vagy"</string>
     <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Fogópont"</string>
-    <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) -->
-    <skip />
+    <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Billentyűzetbeállítások"</string>
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Navigáció a billentyűzet segítségével"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Billentyűparancsok megismerése"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Navigálás az érintőpaddal"</string>
@@ -1464,8 +1477,6 @@
     <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Alkalmazás által biztosított"</string>
     <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Kijelző"</string>
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Ismeretlen"</string>
-    <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) -->
-    <skip />
-    <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) -->
-    <skip />
+    <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"Mozaikok visszaállítása"</string>
+    <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"Visszaállítja a mozaikok eredeti sorrendjét és méretét?"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml
index 92da53b..8fb1347 100644
--- a/packages/SystemUI/res/values-hy/strings.xml
+++ b/packages/SystemUI/res/values-hy/strings.xml
@@ -136,11 +136,17 @@
     <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Դուք ներկայումս տեսագրում եք <xliff:g id="APP_NAME">%1$s</xliff:g> հավելվածը"</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Կանգնեցնել տեսագրումը"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Միացված է էկրանի ցուցադրումը"</string>
+    <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
+    <skip />
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Դադարեցնե՞լ էկրանի ցուցադրումը"</string>
+    <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
+    <skip />
     <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Դուք ներկայումս կիսվում եք ձեր էկրանով <xliff:g id="HOST_APP_NAME">%1$s</xliff:g> հավելվածի հետ"</string>
     <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Դուք ներկայումս կիսվում եք ձեր էկրանով հավելվածի հետ"</string>
     <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"Դուք ներկայումս կիսվում եք <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> հավելվածով"</string>
     <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"Դուք ներկայումս կիսվում եք հավելվածով"</string>
+    <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
+    <skip />
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Դադարեցնել էկրանի ցուցադրումը"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Էկրանի հեռարձակում"</string>
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Կանգնեցնե՞լ հեռարձակումը"</string>
@@ -515,6 +521,10 @@
     <string name="communal_widget_picker_title" msgid="1953369090475731663">"Կողպէկրանի վիջեթներ"</string>
     <string name="communal_widget_picker_description" msgid="490515450110487871">"Բոլորը կարող են դիտել ձեր կողպէկրանի վիջեթները, նույնիսկ եթե պլանշետը կողպված է"</string>
     <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"չեղարկել վիջեթի ընտրությունը"</string>
+    <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
+    <skip />
+    <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
+    <skip />
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Կողպէկրանի վիջեթներ"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Վիջեթի միջոցով հավելված բացելու համար դուք պետք է հաստատեք ձեր ինքնությունը։ Նաև նկատի ունեցեք, որ ցանկացած ոք կարող է դիտել վիջեթները, նույնիսկ երբ ձեր պլանշետը կողպված է։ Որոշ վիջեթներ կարող են նախատեսված չլինել ձեր կողպէկրանի համար, և այստեղ դրանց ավելացնելը կարող է վտանգավոր լինել։"</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Եղավ"</string>
@@ -571,6 +581,10 @@
     <string name="clear_all_notifications_text" msgid="348312370303046130">"Մաքրել բոլորը"</string>
     <string name="manage_notifications_text" msgid="6885645344647733116">"Կառավարել"</string>
     <string name="manage_notifications_history_text" msgid="57055985396576230">"Պատմություն"</string>
+    <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
+    <skip />
+    <!-- no translation found for notification_history_button_description (1578657591405033383) -->
+    <skip />
     <string name="notification_section_header_incoming" msgid="850925217908095197">"Նոր"</string>
     <string name="notification_section_header_gentle" msgid="6804099527336337197">"Անձայն"</string>
     <string name="notification_section_header_alerting" msgid="5581175033680477651">"Ծանուցումներ"</string>
@@ -852,8 +866,8 @@
     <string name="group_system_lock_screen" msgid="7391191300363416543">"Կողպէկրան"</string>
     <string name="group_system_quick_memo" msgid="3764560265935722903">"Ստեղծել նշում"</string>
     <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Բազմախնդրություն"</string>
-    <string name="system_multitasking_rhs" msgid="8714224917276297810">"Աջ մասում օգտագործեք տրոհված էկրանն այս հավելվածի հետ"</string>
-    <string name="system_multitasking_lhs" msgid="8402954791206308783">"Ձախ մասում օգտագործեք տրոհված էկրանն այս հավելվածի հետ"</string>
+    <string name="system_multitasking_rhs" msgid="8714224917276297810">"Տրոհել էկրանը և տեղավորել այս հավելվածը աջում"</string>
+    <string name="system_multitasking_lhs" msgid="8402954791206308783">"Տրոհել էկրանը և տեղավորել այս հավելվածը ձախում"</string>
     <string name="system_multitasking_full_screen" msgid="336048080383640562">"Տրոհված էկրանից անցնել լիաէկրան ռեժիմ"</string>
     <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Անցեք աջ կողմի կամ ներքևի հավելվածին տրոհված էկրանի միջոցով"</string>
     <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Անցեք աջ կողմի կամ վերևի հավելվածին տրոհված էկրանի միջոցով"</string>
@@ -1405,8 +1419,7 @@
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ծավալել պատկերակը"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"կամ"</string>
     <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Տեղափոխման նշիչ"</string>
-    <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) -->
-    <skip />
+    <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Ստեղնաշարի կարգավորումներ"</string>
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Կողմնորոշվեք ձեր ստեղնաշարի օգնությամբ"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Սովորեք օգտագործել ստեղնային դյուրանցումները"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Կողմնորոշվեք ձեր հպահարթակի օգնությամբ"</string>
@@ -1438,7 +1451,7 @@
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Տան կառավարման տարրեր"</string>
     <string name="home_controls_dream_description" msgid="4644150952104035789">"Տան կառավարման տարրերը դարձրեք էկրանապահ"</string>
     <string name="volume_undo_action" msgid="5815519725211877114">"Հետարկել"</string>
-    <string name="back_edu_toast_content" msgid="4530314597378982956">"Հետ գնալու համար հպահարթակի վրա երեք մատը սահեցրեք ձախ կամ աջ"</string>
+    <string name="back_edu_toast_content" msgid="4530314597378982956">"Հետ գնալու համար երեք մատը հպահարթակի վրա սահեցրեք ձախ կամ աջ"</string>
     <string name="home_edu_toast_content" msgid="3381071147871955415">"Հիմնական էկրան անցնելու համար հպահարթակի վրա երեք մատը սահեցրեք ձախ կամ աջ"</string>
     <string name="overview_edu_toast_content" msgid="5797030644017804518">"Վերջերս օգտագործված հավելվածները դիտելու համար երեք մատը սահեցրեք վերև և սեղմած պահեք"</string>
     <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"Բոլոր հավելվածները դիտելու համար սեղմեք գործողության ստեղնը ստեղնաշարի վրա"</string>
@@ -1464,8 +1477,6 @@
     <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Տրամադրվել են հավելվածների կողմից"</string>
     <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Էկրան"</string>
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Անհայտ"</string>
-    <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) -->
-    <skip />
-    <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) -->
-    <skip />
+    <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"Սալիկների վերակայում"</string>
+    <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"Վերականգնե՞լ սալիկների սկզբնական դասավորությունն ու չափսերը"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml
index d054405..0b316be 100644
--- a/packages/SystemUI/res/values-in/strings.xml
+++ b/packages/SystemUI/res/values-in/strings.xml
@@ -136,11 +136,17 @@
     <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Anda sedang merekam <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Berhenti merekam"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Membagikan layar"</string>
+    <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
+    <skip />
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Hentikan berbagi layar?"</string>
+    <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
+    <skip />
     <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Anda sedang berbagi seluruh layar dengan <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>"</string>
     <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Anda sedang berbagi seluruh layar dengan aplikasi"</string>
     <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"Anda sedang berbagi <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>"</string>
     <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"Anda sedang berbagi aplikasi"</string>
+    <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
+    <skip />
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Berhenti berbagi"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Mentransmisikan layar"</string>
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Hentikan transmisi?"</string>
@@ -515,6 +521,10 @@
     <string name="communal_widget_picker_title" msgid="1953369090475731663">"Widget layar kunci"</string>
     <string name="communal_widget_picker_description" msgid="490515450110487871">"Siapa saja dapat melihat widget di layar kunci Anda, meskipun tablet terkunci."</string>
     <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"batalkan pilihan widget"</string>
+    <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
+    <skip />
+    <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
+    <skip />
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Widget layar kunci"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Untuk membuka aplikasi menggunakan widget, Anda perlu memverifikasi diri Anda. Selain itu, harap ingat bahwa siapa saja dapat melihatnya, bahkan saat tablet Anda terkunci. Beberapa widget mungkin tidak dirancang untuk layar kunci Anda dan mungkin tidak aman untuk ditambahkan di sini."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Oke"</string>
@@ -571,6 +581,10 @@
     <string name="clear_all_notifications_text" msgid="348312370303046130">"Hapus semua"</string>
     <string name="manage_notifications_text" msgid="6885645344647733116">"Kelola"</string>
     <string name="manage_notifications_history_text" msgid="57055985396576230">"Histori"</string>
+    <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
+    <skip />
+    <!-- no translation found for notification_history_button_description (1578657591405033383) -->
+    <skip />
     <string name="notification_section_header_incoming" msgid="850925217908095197">"Baru"</string>
     <string name="notification_section_header_gentle" msgid="6804099527336337197">"Senyap"</string>
     <string name="notification_section_header_alerting" msgid="5581175033680477651">"Notifikasi"</string>
@@ -1405,8 +1419,7 @@
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ikon luaskan"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"atau"</string>
     <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Handel geser"</string>
-    <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) -->
-    <skip />
+    <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Setelan Keyboard"</string>
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Menavigasi menggunakan keyboard"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Pelajari pintasan keyboard"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Menavigasi menggunakan touchpad"</string>
@@ -1464,8 +1477,6 @@
     <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Disediakan oleh aplikasi"</string>
     <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Tampilan"</string>
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Tidak diketahui"</string>
-    <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) -->
-    <skip />
-    <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) -->
-    <skip />
+    <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"Reset kartu"</string>
+    <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"Reset kartu ke urutan dan ukuran default?"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml
index 607f48f..700ae58 100644
--- a/packages/SystemUI/res/values-is/strings.xml
+++ b/packages/SystemUI/res/values-is/strings.xml
@@ -136,11 +136,17 @@
     <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Þú ert að taka upp <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Stöðva upptöku"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Deilir skjá"</string>
+    <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
+    <skip />
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Hætta að deila skjá?"</string>
+    <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
+    <skip />
     <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Þú ert að deila öllum skjánum með <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>"</string>
     <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Þú ert að deila öllum skjánum með forriti"</string>
     <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"Þú ert að deila <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>"</string>
     <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"Þú ert að deila forriti"</string>
+    <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
+    <skip />
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Hætta að deila"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Varpar skjá"</string>
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Hætta að varpa?"</string>
@@ -515,6 +521,10 @@
     <string name="communal_widget_picker_title" msgid="1953369090475731663">"Græjur á lásskjá"</string>
     <string name="communal_widget_picker_description" msgid="490515450110487871">"Hver sem er getur séð græjur á lásskjánum þínum, jafnvel þótt spjaldtölvan sé læst."</string>
     <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"afturkalla val á græju"</string>
+    <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
+    <skip />
+    <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
+    <skip />
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Græjur fyrir lásskjá"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Þú þarft að staðfesta að þetta sért þú til að geta opnað forrit með græju. Hafðu einnig í huga að hver sem er getur skoðað þær, jafnvel þótt spjaldtölvan sé læst. Sumar græjur eru hugsanlega ekki ætlaðar fyrir lásskjá og því gæti verið óöruggt að bæta þeim við hér."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Ég skil"</string>
@@ -555,7 +565,7 @@
     <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"Varpa öllum skjánum"</string>
     <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"Þegar þú varpar öllum skjánum þá er allt á skjánum sýnilegt. Passaðu því upp á aðgangsorð, greiðsluupplýsingar, skilaboð, myndir, hljóð og myndskeið."</string>
     <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"Þegar þú varpar forriti er allt sem sést eða er spilað í því forriti sýnilegt. Passaðu því upp á aðgangsorð, greiðsluupplýsingar, skilaboð, myndir, hljóð og myndskeið."</string>
-    <string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"Senda út skjá"</string>
+    <string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"Varpa skjá"</string>
     <string name="media_projection_entry_cast_app_selector_title" msgid="6323062146661922387">"Velja forrit til að varpa"</string>
     <string name="media_projection_entry_generic_permission_dialog_title" msgid="4519802931547483628">"Byrja að deila?"</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_entire_screen" msgid="5407906851409410209">"Þegar þú deilir, tekur upp eða varpar hefur Android aðgang að öllu sem sést á skjánum eða spilast í tækinu. Passaðu því upp á aðgangsorð, greiðsluupplýsingar, skilaboð, myndir, hljóð og myndskeið."</string>
@@ -571,6 +581,10 @@
     <string name="clear_all_notifications_text" msgid="348312370303046130">"Hreinsa allt"</string>
     <string name="manage_notifications_text" msgid="6885645344647733116">"Stjórna"</string>
     <string name="manage_notifications_history_text" msgid="57055985396576230">"Ferill"</string>
+    <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
+    <skip />
+    <!-- no translation found for notification_history_button_description (1578657591405033383) -->
+    <skip />
     <string name="notification_section_header_incoming" msgid="850925217908095197">"Nýtt"</string>
     <string name="notification_section_header_gentle" msgid="6804099527336337197">"Hljóðlaust"</string>
     <string name="notification_section_header_alerting" msgid="5581175033680477651">"Tilkynningar"</string>
@@ -1405,8 +1419,7 @@
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Stækka tákn"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"eða"</string>
     <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Dragkló"</string>
-    <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) -->
-    <skip />
+    <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Stillingar lyklaborðs"</string>
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Flettu með því að nota lyklaborðið"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Kynntu þér flýtilykla"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Flettu með því að nota snertiflötinn"</string>
@@ -1464,8 +1477,6 @@
     <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Frá forritum"</string>
     <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Skjár"</string>
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Óþekkt"</string>
-    <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) -->
-    <skip />
-    <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) -->
-    <skip />
+    <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"Endurstilla flísar"</string>
+    <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"Endurstilla flísar í upphaflega röð og stærð?"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index 0cbdb86..e7734fa 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -136,11 +136,17 @@
     <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Al momento stai registrando <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Interrompi registrazione"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Condivisione dello schermo in corso"</string>
+    <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
+    <skip />
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Vuoi interrompere la condivisione dello schermo?"</string>
+    <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
+    <skip />
     <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Al momento stai condividendo l\'intero schermo con <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>"</string>
     <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Al momento stai condividendo l\'intero schermo con un\'app"</string>
     <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"Al momento stai condividendo <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>"</string>
     <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"Al momento stai condividendo un\'app"</string>
+    <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
+    <skip />
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Interrompi condivisione"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Trasmissione dello schermo in corso…"</string>
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Vuoi interrompere la trasmissione?"</string>
@@ -515,6 +521,10 @@
     <string name="communal_widget_picker_title" msgid="1953369090475731663">"Widget della schermata di blocco"</string>
     <string name="communal_widget_picker_description" msgid="490515450110487871">"Chiunque può visualizzare i widget sulla tua schermata di blocco, anche se il tablet è bloccato."</string>
     <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"deseleziona widget"</string>
+    <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
+    <skip />
+    <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
+    <skip />
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Widget della schermata di blocco"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Per aprire un\'app utilizzando un widget, dovrai verificare la tua identità. Inoltre tieni presente che chiunque può vederlo, anche quando il tablet è bloccato. Alcuni widget potrebbero non essere stati progettati per la schermata di blocco e potrebbe non essere sicuro aggiungerli qui."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Ok"</string>
@@ -571,6 +581,10 @@
     <string name="clear_all_notifications_text" msgid="348312370303046130">"Cancella tutto"</string>
     <string name="manage_notifications_text" msgid="6885645344647733116">"Gestisci"</string>
     <string name="manage_notifications_history_text" msgid="57055985396576230">"Cronologia"</string>
+    <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
+    <skip />
+    <!-- no translation found for notification_history_button_description (1578657591405033383) -->
+    <skip />
     <string name="notification_section_header_incoming" msgid="850925217908095197">"Nuove"</string>
     <string name="notification_section_header_gentle" msgid="6804099527336337197">"Notifiche silenziose"</string>
     <string name="notification_section_header_alerting" msgid="5581175033680477651">"Notifiche"</string>
@@ -1405,8 +1419,7 @@
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Icona Espandi"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"oppure"</string>
     <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Punto di trascinamento"</string>
-    <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) -->
-    <skip />
+    <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Impostazioni tastiera"</string>
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Naviga usando la tastiera"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Informazioni sulle scorciatoie da tastiera"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Naviga usando il touchpad"</string>
@@ -1438,7 +1451,7 @@
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Controlli della casa"</string>
     <string name="home_controls_dream_description" msgid="4644150952104035789">"Accedi rapidamente ai controlli della casa dal salvaschermo"</string>
     <string name="volume_undo_action" msgid="5815519725211877114">"Annulla"</string>
-    <string name="back_edu_toast_content" msgid="4530314597378982956">"Per tornare indietro, scorri verso sinistra o verso destra con tre dita sul touchpad."</string>
+    <string name="back_edu_toast_content" msgid="4530314597378982956">"Per tornare indietro, scorri verso sinistra o destra con tre dita sul touchpad."</string>
     <string name="home_edu_toast_content" msgid="3381071147871955415">"Per andare alla schermata Home, scorri verso l\'alto con tre dita sul touchpad"</string>
     <string name="overview_edu_toast_content" msgid="5797030644017804518">"Per visualizzare le app recenti, scorri verso l\'alto e tieni premuto con tre dita sul touchpad"</string>
     <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"Per visualizzare tutte le tue app, premi il tasto azione sulla tastiera"</string>
@@ -1464,8 +1477,6 @@
     <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Forniti dalle app"</string>
     <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Display"</string>
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Sconosciuti"</string>
-    <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) -->
-    <skip />
-    <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) -->
-    <skip />
+    <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"Reimposta riquadri"</string>
+    <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"Ripristinare l\'ordine e le dimensioni originali dei riquadri?"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index 465d8f5..553eaae 100644
--- a/packages/SystemUI/res/values-iw/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
@@ -136,11 +136,17 @@
     <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"מתבצעת כרגע הקלטה של <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"הפסקת ההקלטה"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"שיתוף המסך מתבצע"</string>
+    <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
+    <skip />
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"להפסיק את שיתוף המסך?"</string>
+    <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
+    <skip />
     <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"מתבצע כרגע שיתוף של כל המסך שלך עם <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>"</string>
     <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"מתבצע כרגע שיתוף של כל המסך שלך עם אפליקציה"</string>
     <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"מתבצע כרגע שיתוף של <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>"</string>
     <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"מתבצע כרגע שיתוף של אפליקציה"</string>
+    <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
+    <skip />
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"הפסקת השיתוף"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"‏הפעלת Cast של המסך מתבצעת"</string>
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"‏להפסיק את פעולת ה-Cast?"</string>
@@ -515,6 +521,10 @@
     <string name="communal_widget_picker_title" msgid="1953369090475731663">"ווידג\'טים במסך הנעילה"</string>
     <string name="communal_widget_picker_description" msgid="490515450110487871">"כולם יכולים לראות את הווידג\'טים במסך הנעילה שלך, גם אם הטאבלט נעול."</string>
     <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"ביטול הבחירה בווידג\'ט"</string>
+    <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
+    <skip />
+    <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
+    <skip />
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"ווידג\'טים במסך הנעילה"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"כדי לפתוח אפליקציה באמצעות ווידג\'ט, עליך לאמת את זהותך. בנוסף, כדאי לזכור שכל אחד יכול לראות את הווידג\'טים גם כשהטאבלט שלך נעול. יכול להיות שחלק מהווידג\'טים לא נועדו למסך הנעילה ושלא בטוח להוסיף אותם לכאן."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"הבנתי"</string>
@@ -571,8 +581,12 @@
     <string name="clear_all_notifications_text" msgid="348312370303046130">"ניקוי הכול"</string>
     <string name="manage_notifications_text" msgid="6885645344647733116">"ניהול"</string>
     <string name="manage_notifications_history_text" msgid="57055985396576230">"היסטוריה"</string>
+    <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
+    <skip />
+    <!-- no translation found for notification_history_button_description (1578657591405033383) -->
+    <skip />
     <string name="notification_section_header_incoming" msgid="850925217908095197">"התראות חדשות"</string>
-    <string name="notification_section_header_gentle" msgid="6804099527336337197">"שקטות"</string>
+    <string name="notification_section_header_gentle" msgid="6804099527336337197">"מצב שקט"</string>
     <string name="notification_section_header_alerting" msgid="5581175033680477651">"התראות"</string>
     <string name="notification_section_header_conversations" msgid="821834744538345661">"שיחות"</string>
     <string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"ניקוי כל ההתראות השקטות"</string>
@@ -843,13 +857,13 @@
     <string name="group_system_access_system_app_shortcuts" msgid="8562482996626694026">"הצגת מקשי הקיצור"</string>
     <string name="group_system_go_back" msgid="2730322046244918816">"חזרה"</string>
     <string name="group_system_access_home_screen" msgid="4130366993484706483">"מעבר למסך הבית"</string>
-    <string name="group_system_overview_open_apps" msgid="5659958952937994104">"הצגת האפליקציות האחרונות"</string>
+    <string name="group_system_overview_open_apps" msgid="5659958952937994104">"צפייה באפליקציות האחרונות"</string>
     <string name="group_system_cycle_forward" msgid="5478663965957647805">"דפדוף קדימה באפליקציות האחרונות"</string>
     <string name="group_system_cycle_back" msgid="8194102916946802902">"דפדוף אחורה באפליקציות האחרונות"</string>
     <string name="group_system_access_all_apps_search" msgid="1553588630154197469">"פתיחה של רשימת האפליקציות"</string>
     <string name="group_system_access_system_settings" msgid="8731721963449070017">"פתיחת ההגדרות"</string>
-    <string name="group_system_access_google_assistant" msgid="7210074957915968110">"‏לפתיחת Google Assistant"</string>
-    <string name="group_system_lock_screen" msgid="7391191300363416543">"מסך הנעילה"</string>
+    <string name="group_system_access_google_assistant" msgid="7210074957915968110">"‏פתיחת Google Assistant"</string>
+    <string name="group_system_lock_screen" msgid="7391191300363416543">"נעילת המסך"</string>
     <string name="group_system_quick_memo" msgid="3764560265935722903">"כתיבת הערה"</string>
     <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"ריבוי משימות"</string>
     <string name="system_multitasking_rhs" msgid="8714224917276297810">"שימוש במסך מפוצל כשהאפליקציה הנוכחית בצד ימין"</string>
@@ -1405,8 +1419,7 @@
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"סמל ההרחבה"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"או"</string>
     <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"נקודת האחיזה לגרירה"</string>
-    <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) -->
-    <skip />
+    <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"הגדרות המקלדת"</string>
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"ניווט באמצעות המקלדת"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"מידע על מקשי קיצור"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"ניווט באמצעות לוח המגע"</string>
@@ -1445,7 +1458,7 @@
     <string name="redacted_notification_single_line_title" msgid="212019960919261670">"מצונזר"</string>
     <string name="redacted_notification_single_line_text" msgid="8684166405005242945">"צריך לבטל את הנעילה כדי לראות"</string>
     <string name="contextual_education_dialog_title" msgid="4630392552837487324">"חינוך בהתאם להקשר"</string>
-    <string name="back_edu_notification_title" msgid="5624780717751357278">"איך להשתמש בלוח המגע כדי לחזור אחורה"</string>
+    <string name="back_edu_notification_title" msgid="5624780717751357278">"אפשר להשתמש בלוח המגע כדי לחזור אחורה"</string>
     <string name="back_edu_notification_content" msgid="2497557451540954068">"מחליקים ימינה או שמאלה עם שלוש אצבעות. ניתן להקיש כדי לקבל מידע נוסף על התנועות."</string>
     <string name="home_edu_notification_title" msgid="6097902076909654045">"איך להשתמש בלוח המגע כדי לעבור למסך הבית"</string>
     <string name="home_edu_notification_content" msgid="6631697734535766588">"מחליקים למעלה עם שלוש אצבעות. ניתן להקיש כדי לקבל מידע נוסף על התנועות."</string>
@@ -1464,8 +1477,6 @@
     <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"מסופקים על ידי אפליקציות"</string>
     <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"מסך"</string>
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"לא ידוע"</string>
-    <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) -->
-    <skip />
-    <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) -->
-    <skip />
+    <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"איפוס המשבצות"</string>
+    <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"לאפס את המשבצות לסדר ולגודל המקורי שלהן?"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index 6df9f73..a6fffee 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -136,11 +136,17 @@
     <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"現在、<xliff:g id="APP_NAME">%1$s</xliff:g>を録画しています"</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"録画を停止"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"画面を共有しています"</string>
+    <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
+    <skip />
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"画面の共有を停止しますか?"</string>
+    <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
+    <skip />
     <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"現在、画面全体を<xliff:g id="HOST_APP_NAME">%1$s</xliff:g>と共有しています"</string>
     <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"現在、画面全体をアプリと共有しています"</string>
     <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"現在、<xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>を共有しています"</string>
     <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"現在、アプリを共有しています"</string>
+    <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
+    <skip />
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"共有を停止"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"画面をキャストしています"</string>
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"キャストを停止しますか?"</string>
@@ -515,6 +521,10 @@
     <string name="communal_widget_picker_title" msgid="1953369090475731663">"ロック画面ウィジェット"</string>
     <string name="communal_widget_picker_description" msgid="490515450110487871">"タブレットがロックされていても、ロック画面のウィジェットは誰でも確認できます。"</string>
     <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"ウィジェットの選択を解除する"</string>
+    <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
+    <skip />
+    <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
+    <skip />
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"ロック画面ウィジェット"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"ウィジェットを使用してアプリを起動するには、本人確認が必要です。タブレットがロックされた状態でも他のユーザーにウィジェットが表示されますので、注意してください。一部のウィジェットについてはロック画面での使用を想定していないため、ロック画面への追加は危険な場合があります。"</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"OK"</string>
@@ -571,6 +581,8 @@
     <string name="clear_all_notifications_text" msgid="348312370303046130">"すべて消去"</string>
     <string name="manage_notifications_text" msgid="6885645344647733116">"管理"</string>
     <string name="manage_notifications_history_text" msgid="57055985396576230">"履歴"</string>
+    <string name="notification_settings_button_description" msgid="2441994740884163889">"通知設定"</string>
+    <string name="notification_history_button_description" msgid="1578657591405033383">"通知履歴"</string>
     <string name="notification_section_header_incoming" msgid="850925217908095197">"新着"</string>
     <string name="notification_section_header_gentle" msgid="6804099527336337197">"サイレント"</string>
     <string name="notification_section_header_alerting" msgid="5581175033680477651">"通知"</string>
@@ -1405,8 +1417,7 @@
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"開くアイコン"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"または"</string>
     <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"ドラッグ ハンドル"</string>
-    <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) -->
-    <skip />
+    <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"キーボードの設定"</string>
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"キーボードを使用して移動する"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"キーボード ショートカットの詳細"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"タッチパッドを使用して移動する"</string>
@@ -1438,7 +1449,7 @@
     <string name="home_controls_dream_label" msgid="6567105701292324257">"ホーム コントロール"</string>
     <string name="home_controls_dream_description" msgid="4644150952104035789">"ホーム コントロールにスクリーンセーバーからすばやくアクセス"</string>
     <string name="volume_undo_action" msgid="5815519725211877114">"元に戻す"</string>
-    <string name="back_edu_toast_content" msgid="4530314597378982956">"戻るには、3 本の指でタッチパッドを左右にスワイプします"</string>
+    <string name="back_edu_toast_content" msgid="4530314597378982956">"戻るには、3 本の指でタッチパッドを左か右にスワイプします"</string>
     <string name="home_edu_toast_content" msgid="3381071147871955415">"ホームに移動するには、3 本の指でタッチパッドを上にスワイプします"</string>
     <string name="overview_edu_toast_content" msgid="5797030644017804518">"最近使ったアプリを表示するには、3 本の指でタッチパッドを上にスワイプして長押しします"</string>
     <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"すべてのアプリを表示するには、キーボードのアクションキーを押してください"</string>
@@ -1464,8 +1475,6 @@
     <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"アプリから提供"</string>
     <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"ディスプレイ"</string>
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"不明"</string>
-    <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) -->
-    <skip />
-    <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) -->
-    <skip />
+    <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"タイルのリセット"</string>
+    <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"タイルを元の順序とサイズにリセットしますか?"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ka/strings.xml b/packages/SystemUI/res/values-ka/strings.xml
index b30c748..9608253 100644
--- a/packages/SystemUI/res/values-ka/strings.xml
+++ b/packages/SystemUI/res/values-ka/strings.xml
@@ -136,11 +136,17 @@
     <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"თქვენ ამჟამად იწერთ <xliff:g id="APP_NAME">%1$s</xliff:g>-ს"</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"ჩაწერის შეწყვეტა"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"მიმდინარეობს ეკრანის გაზიარება"</string>
+    <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
+    <skip />
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"გსურთ ეკრანის გაზიარების შეწყვეტა?"</string>
+    <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
+    <skip />
     <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"თქვენ ამჟამად უზიარებთ თქვენს მთლიან ეკრანს <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>-ს"</string>
     <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"თქვენ ამჟამად უზიარებთ თქვენს მთლიან ეკრანს აპს"</string>
     <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"თქვენ ამჟამად აზიარებთ <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>-ს"</string>
     <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"თქვენ ამჟამად აზიარებთ აპს"</string>
+    <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
+    <skip />
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"გაზიარების შეწყვეტა"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"მიმდინარეობს ეკრანის ტრანსლირება"</string>
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"გსურთ ტრანსლირების შეწყვეტა?"</string>
@@ -515,6 +521,10 @@
     <string name="communal_widget_picker_title" msgid="1953369090475731663">"ჩაკეტილი ეკრანის ვიჯეტები"</string>
     <string name="communal_widget_picker_description" msgid="490515450110487871">"ნებისმიერს შეუძლია თქვენს ჩაკეტილ ეკრანზე ვიჯეტების ნახვა, თუნდაც ტაბლეტი ჩაკეტილი იყოს."</string>
     <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"ვიჯეტის არჩევის გაუქმება"</string>
+    <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
+    <skip />
+    <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
+    <skip />
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"დაბლოკილი ეკრანის ვიჯეტები"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"უნდა დაადასტუროთ თქვენი ვინაობა, რათა გახსნათ აპი ვიჯეტის გამოყენებით. გაითვალისწინეთ, რომ ნებისმიერს შეუძლია მათი ნახვა, მაშინაც კი, როცა ტაბლეტი დაბლოკილია. ზოგი ვიჯეტი შეიძლება არ იყოს გათვლილი თქვენი დაბლოკილი ეკრანისთვის და მათი აქ დამატება შეიძლება სახიფათო იყოს."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"გასაგებია"</string>
@@ -571,6 +581,10 @@
     <string name="clear_all_notifications_text" msgid="348312370303046130">"ყველას გასუფთავება"</string>
     <string name="manage_notifications_text" msgid="6885645344647733116">"მართვა"</string>
     <string name="manage_notifications_history_text" msgid="57055985396576230">"ისტორია"</string>
+    <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
+    <skip />
+    <!-- no translation found for notification_history_button_description (1578657591405033383) -->
+    <skip />
     <string name="notification_section_header_incoming" msgid="850925217908095197">"ახალი"</string>
     <string name="notification_section_header_gentle" msgid="6804099527336337197">"ჩუმი"</string>
     <string name="notification_section_header_alerting" msgid="5581175033680477651">"შეტყობინებები"</string>
@@ -1405,8 +1419,7 @@
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"ხატულის გაფართოება"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ან"</string>
     <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"სახელური ჩავლებისთვის"</string>
-    <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) -->
-    <skip />
+    <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"კლავიატურის პარამეტრები"</string>
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"ნავიგაცია კლავიატურის გამოყენებით"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"კლავიატურის მალსახმობების სწავლა"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"ნავიგაცია სენსორული პანელის გამოყენებით"</string>
@@ -1464,8 +1477,6 @@
     <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"მოწოდებულია აპების მიერ"</string>
     <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"ეკრანი"</string>
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"უცნობი"</string>
-    <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) -->
-    <skip />
-    <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) -->
-    <skip />
+    <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"მოზაიკის ფილების გადაყენება"</string>
+    <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"გსურთ მოზაიკის ფილების გადაყენება მათ ორიგინალ წყობაზე და ზომებზე?"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml
index c9517ec..0456fa8 100644
--- a/packages/SystemUI/res/values-kk/strings.xml
+++ b/packages/SystemUI/res/values-kk/strings.xml
@@ -136,11 +136,17 @@
     <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Қазір қолданбадағы (<xliff:g id="APP_NAME">%1$s</xliff:g>) контентті жазып жатырсыз."</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Жазуды тоқтату"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Экранды бөлісіп жатыр."</string>
+    <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
+    <skip />
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Экранды бөлісуді тоқтатасыз ба?"</string>
+    <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
+    <skip />
     <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Қазір бүкіл экранды қолданбамен (<xliff:g id="HOST_APP_NAME">%1$s</xliff:g>) бөлісіп жатырсыз."</string>
     <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Қазір бүкіл экранды қолданбамен бөлісіп жатырсыз."</string>
     <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"Қазір қолданбадағы (<xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>) контентті бөлісіп жатырсыз."</string>
     <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"Қазір қолданбаны бөлісіп жатырсыз."</string>
+    <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
+    <skip />
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Бөлісуді тоқтату"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Экранды трансляциялап жатырсыз."</string>
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Трансляциялау тоқтасын ба?"</string>
@@ -515,6 +521,10 @@
     <string name="communal_widget_picker_title" msgid="1953369090475731663">"Құлып экранының виджеттері"</string>
     <string name="communal_widget_picker_description" msgid="490515450110487871">"Планшет құлыпталып тұрса да, құлып экранындағы виджеттерді кез келген адам көре алады."</string>
     <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"виджетті таңдаудан алу"</string>
+    <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
+    <skip />
+    <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
+    <skip />
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Құлып экранының виджеттері"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Қолданбаны виджет көмегімен ашу үшін жеке басыңызды растауыңыз керек. Сондай-ақ басқалар оларды планшетіңіз құлыптаулы кезде де көре алатынын ескеріңіз. Кейбір виджеттер құлып экранына арналмаған болады, сондықтан оларды мұнда қосу қауіпсіз болмауы мүмкін."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Түсінікті"</string>
@@ -571,6 +581,10 @@
     <string name="clear_all_notifications_text" msgid="348312370303046130">"Барлығын тазарту"</string>
     <string name="manage_notifications_text" msgid="6885645344647733116">"Басқару"</string>
     <string name="manage_notifications_history_text" msgid="57055985396576230">"Тарих"</string>
+    <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
+    <skip />
+    <!-- no translation found for notification_history_button_description (1578657591405033383) -->
+    <skip />
     <string name="notification_section_header_incoming" msgid="850925217908095197">"Жаңа"</string>
     <string name="notification_section_header_gentle" msgid="6804099527336337197">"Үнсіз"</string>
     <string name="notification_section_header_alerting" msgid="5581175033680477651">"Хабарландырулар"</string>
@@ -852,8 +866,8 @@
     <string name="group_system_lock_screen" msgid="7391191300363416543">"Экранды құлыптау"</string>
     <string name="group_system_quick_memo" msgid="3764560265935722903">"Ескертпе жазу"</string>
     <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Мультитаскинг"</string>
-    <string name="system_multitasking_rhs" msgid="8714224917276297810">"Ағымдағы қолданба оң жақта тұратын бөлінген экранды пайдаланыңыз"</string>
-    <string name="system_multitasking_lhs" msgid="8402954791206308783">"Ағымдағы қолданба сол жақта тұратын бөлінген экранды пайдаланыңыз"</string>
+    <string name="system_multitasking_rhs" msgid="8714224917276297810">"Қолданбаны бөлінген экранның оң жағынан пайдалану"</string>
+    <string name="system_multitasking_lhs" msgid="8402954791206308783">"Қолданбаны бөлінген экранның сол жағынан пайдалану"</string>
     <string name="system_multitasking_full_screen" msgid="336048080383640562">"Бөлінген экран режимінен толық экран режиміне ауысу"</string>
     <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Бөлінген экранда оң не төмен жақтағы қолданбаға ауысу"</string>
     <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Бөлінген экранда сол не жоғары жақтағы қолданбаға ауысу"</string>
@@ -1405,8 +1419,7 @@
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Жаю белгішесі"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"немесе"</string>
     <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Сүйрейтін тетік"</string>
-    <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) -->
-    <skip />
+    <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Пернетақта параметрлері"</string>
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Пернетақтамен жұмыс істеңіз"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Перне тіркесімдерін үйреніңіз."</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Сенсорлық тақтамен жұмыс істеңіз"</string>
@@ -1464,8 +1477,6 @@
     <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Қолданбалар ұсынған"</string>
     <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Дисплей"</string>
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Белгісіз"</string>
-    <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) -->
-    <skip />
-    <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) -->
-    <skip />
+    <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"Бөлшектерді бастапқы күйге қайтару"</string>
+    <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"Бөлшектерді бастапқы реті мен өлшеміне қайтару керек пе?"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml
index 10a125b..3c33531 100644
--- a/packages/SystemUI/res/values-km/strings.xml
+++ b/packages/SystemUI/res/values-km/strings.xml
@@ -136,11 +136,17 @@
     <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"បច្ចុប្បន្ន អ្នកកំពុងថត <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"ឈប់ថត"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"កំពុងបង្ហាញអេក្រង់"</string>
+    <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
+    <skip />
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"ឈប់បង្ហាញអេក្រង់ឬ?"</string>
+    <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
+    <skip />
     <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"បច្ចុប្បន្ន អ្នកកំពុងបង្ហាញអេក្រង់ទាំងមូលរបស់អ្នកតាមរយៈ <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>"</string>
     <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"បច្ចុប្បន្ន អ្នកកំពុងបង្ហាញអេក្រង់ទាំងមូលរបស់អ្នកតាមរយៈកម្មវិធីមួយ"</string>
     <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"បច្ចុប្បន្ន អ្នកកំពុងបង្ហាញ <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>"</string>
     <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"បច្ចុប្បន្ន អ្នកកំពុងបង្ហាញកម្មវិធីមួយ"</string>
+    <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
+    <skip />
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"ឈប់​បង្ហាញ"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"កំពុង​បញ្ជូន​អេក្រង់"</string>
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"ឈប់បញ្ជូនឬ?"</string>
@@ -515,6 +521,10 @@
     <string name="communal_widget_picker_title" msgid="1953369090475731663">"ធាតុ​ក្រាហ្វិកអេក្រង់ចាក់សោ"</string>
     <string name="communal_widget_picker_description" msgid="490515450110487871">"អ្នកគ្រប់គ្នាអាចមើលធាតុក្រាហ្វិកលើអេក្រង់ចាក់សោរបស់អ្នក ទោះបីជាថេប្លេតរបស់អ្នកត្រូវបានចាក់សោក៏ដោយ។"</string>
     <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"ដក​ការ​ជ្រើសរើសធាតុ​ក្រាហ្វិក"</string>
+    <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
+    <skip />
+    <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
+    <skip />
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"ធាតុ​ក្រាហ្វិកលើអេក្រង់ចាក់សោ"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"ដើម្បីបើកកម្មវិធីដោយប្រើធាតុ​ក្រាហ្វិក អ្នកនឹងត្រូវផ្ទៀងផ្ទាត់ថាជាអ្នក។ ទន្ទឹមនឹងនេះ សូមចងចាំថា នរណាក៏អាចមើលធាតុក្រាហ្វិកបាន សូម្បីពេលថេប្លេតរបស់អ្នកជាប់សោក៏ដោយ។ ធាតុ​ក្រាហ្វិកមួយចំនួនប្រហែលមិនត្រូវបានរចនាឡើងសម្រាប់អេក្រង់ចាក់សោរបស់អ្នកទេ និងមិនមានសុវត្ថិភាពឡើយ បើបញ្ចូលទៅទីនេះ។"</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"យល់ហើយ"</string>
@@ -571,6 +581,10 @@
     <string name="clear_all_notifications_text" msgid="348312370303046130">"សម្អាត​ទាំងអស់"</string>
     <string name="manage_notifications_text" msgid="6885645344647733116">"គ្រប់គ្រង"</string>
     <string name="manage_notifications_history_text" msgid="57055985396576230">"ប្រវត្តិ"</string>
+    <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
+    <skip />
+    <!-- no translation found for notification_history_button_description (1578657591405033383) -->
+    <skip />
     <string name="notification_section_header_incoming" msgid="850925217908095197">"ថ្មី"</string>
     <string name="notification_section_header_gentle" msgid="6804099527336337197">"ស្ងាត់"</string>
     <string name="notification_section_header_alerting" msgid="5581175033680477651">"ការជូនដំណឹង"</string>
@@ -1391,7 +1405,7 @@
     <string name="shortcut_helper_category_system" msgid="462110876978937359">"ប្រព័ន្ធ"</string>
     <string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"ការគ្រប់គ្រង​ប្រព័ន្ធ"</string>
     <string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"កម្មវិធី​ប្រព័ន្ធ"</string>
-    <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"ការដំណើរការបានច្រើន"</string>
+    <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"ការធ្វើកិច្ចការច្រើន"</string>
     <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"កម្មវិធី​ថ្មីៗ"</string>
     <string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"មុខងារ​បំបែកអេក្រង់"</string>
     <string name="shortcut_helper_category_input" msgid="8674018654124839566">"ធាតុចូល"</string>
@@ -1405,8 +1419,7 @@
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"រូបតំណាង \"ពង្រីក\""</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ឬ"</string>
     <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"ដង​អូស"</string>
-    <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) -->
-    <skip />
+    <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"ការកំណត់​ក្ដារចុច"</string>
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"រុករកដោយប្រើក្ដារចុចរបស់អ្នក"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"ស្វែងយល់អំពីផ្លូវកាត់​ក្ដារ​ចុច"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"រុករកដោយប្រើផ្ទាំងប៉ះរបស់អ្នក"</string>
@@ -1464,8 +1477,6 @@
     <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"ផ្ដល់ជូនដោយកម្មវិធី"</string>
     <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"ផ្ទាំងបង្ហាញ"</string>
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"មិនស្គាល់"</string>
-    <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) -->
-    <skip />
-    <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) -->
-    <skip />
+    <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"កំណត់ប្រអប់ឡើងវិញ"</string>
+    <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"កំណត់ប្រអប់ឡើងវិញទៅទំហំ និងលំដាប់ដើមរបស់ប្រអប់ទាំងនោះឬ?"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml
index 3ff0629..dca2e4b 100644
--- a/packages/SystemUI/res/values-kn/strings.xml
+++ b/packages/SystemUI/res/values-kn/strings.xml
@@ -136,11 +136,17 @@
     <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"ನೀವು ಪ್ರಸ್ತುತ <xliff:g id="APP_NAME">%1$s</xliff:g> ಅನ್ನು ರೆಕಾರ್ಡ್ ಮಾಡುತ್ತಿದ್ದೀರಿ"</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"ರೆಕಾರ್ಡಿಂಗ್ ನಿಲ್ಲಿಸಿ"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"ಪರದೆಯನ್ನು ಹಂಚಿಕೊಳ್ಳಲಾಗುತ್ತಿದೆ"</string>
+    <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
+    <skip />
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"ಸ್ಕ್ರೀನ್ ಹಂಚಿಕೊಳ್ಳುವಿಕೆಯನ್ನು ನಿಲ್ಲಿಸಬೇಕೆ?"</string>
+    <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
+    <skip />
     <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"ನೀವು ಪ್ರಸ್ತುತ ನಿಮ್ಮ ಸಂಪೂರ್ಣ ಸ್ಕ್ರೀನ್ ಅನ್ನು <xliff:g id="HOST_APP_NAME">%1$s</xliff:g> ನೊಂದಿಗೆ ಹಂಚಿಕೊಳ್ಳುತ್ತಿದ್ದೀರಿ"</string>
     <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"ನೀವು ಪ್ರಸ್ತುತ ನಿಮ್ಮ ಸಂಪೂರ್ಣ ಸ್ಕ್ರೀನ್ ಅನ್ನು ಆ್ಯಪ್‌ನೊಂದಿಗೆ ಹಂಚಿಕೊಳ್ಳುತ್ತಿದ್ದೀರಿ"</string>
     <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"ನೀವು ಪ್ರಸ್ತುತ <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> ಅನ್ನು ಹಂಚಿಕೊಳ್ಳುತ್ತಿದ್ದೀರಿ"</string>
     <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"ನೀವು ಪ್ರಸ್ತುತ ಆ್ಯಪ್ ಅನ್ನು ಹಂಚಿಕೊಳ್ಳುತ್ತಿದ್ದೀರಿ"</string>
+    <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
+    <skip />
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"ಹಂಚಿಕೊಳ್ಳುವಿಕೆಯನ್ನು ನಿಲ್ಲಿಸಿ"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"ಸ್ಕ್ರೀನ್ ಅನ್ನು ಬಿತ್ತರಿಸಲಾಗುತ್ತಿದೆ"</string>
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"ಬಿತ್ತರಿಸುವುದನ್ನು ನಿಲ್ಲಿಸಬೇಕೆ?"</string>
@@ -515,6 +521,10 @@
     <string name="communal_widget_picker_title" msgid="1953369090475731663">"ಲಾಕ್ ಸ್ಕ್ರೀನ್ ವಿಜೆಟ್‌ಗಳು"</string>
     <string name="communal_widget_picker_description" msgid="490515450110487871">"ನಿಮ್ಮ ಟ್ಯಾಬ್ಲೆಟ್ ಲಾಕ್ ಆಗಿದ್ದರೂ ಸಹ ಯಾರಾದರೂ ನಿಮ್ಮ ಲಾಕ್ ಸ್ಕ್ರೀನ್‌ನಲ್ಲಿ ವಿಜೆಟ್‌ಗಳನ್ನು ವೀಕ್ಷಿಸಬಹುದು."</string>
     <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"ವಿಜೆಟ್ ಅನ್ನು ಆಯ್ಕೆ ಮಾಡಬೇಡಿ"</string>
+    <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
+    <skip />
+    <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
+    <skip />
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"ಲಾಕ್ ಸ್ಕ್ರೀನ್ ವಿಜೆಟ್‌ಗಳು"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"ವಿಜೆಟ್ ಅನ್ನು ಬಳಸಿಕೊಂಡು ಆ್ಯಪ್ ತೆರೆಯಲು, ಇದು ನೀವೇ ಎಂದು ನೀವು ದೃಢೀಕರಿಸಬೇಕಾಗುತ್ತದೆ. ಅಲ್ಲದೆ, ನಿಮ್ಮ ಟ್ಯಾಬ್ಲೆಟ್ ಲಾಕ್ ಆಗಿದ್ದರೂ ಸಹ ಯಾರಾದರೂ ಅವುಗಳನ್ನು ವೀಕ್ಷಿಸಬಹುದು ಎಂಬುದನ್ನು ನೆನಪಿನಲ್ಲಿಡಿ. ಕೆಲವು ವಿಜೆಟ್‌ಗಳು ನಿಮ್ಮ ಲಾಕ್ ಸ್ಕ್ರೀನ್‌ಗಾಗಿ ಉದ್ದೇಶಿಸದೇ ಇರಬಹುದು ಮತ್ತು ಇಲ್ಲಿ ಸೇರಿಸುವುದು ಸುರಕ್ಷಿತವಲ್ಲದಿರಬಹುದು."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"ಅರ್ಥವಾಯಿತು"</string>
@@ -571,6 +581,10 @@
     <string name="clear_all_notifications_text" msgid="348312370303046130">"ಎಲ್ಲವನ್ನೂ ತೆರವುಗೊಳಿಸಿ"</string>
     <string name="manage_notifications_text" msgid="6885645344647733116">"ನಿರ್ವಹಿಸಿ"</string>
     <string name="manage_notifications_history_text" msgid="57055985396576230">"ಇತಿಹಾಸ"</string>
+    <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
+    <skip />
+    <!-- no translation found for notification_history_button_description (1578657591405033383) -->
+    <skip />
     <string name="notification_section_header_incoming" msgid="850925217908095197">"ಹೊಸತು"</string>
     <string name="notification_section_header_gentle" msgid="6804099527336337197">"ನಿಶ್ಶಬ್ದ"</string>
     <string name="notification_section_header_alerting" msgid="5581175033680477651">"ಅಧಿಸೂಚನೆಗಳು"</string>
@@ -1405,8 +1419,7 @@
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"ವಿಸ್ತೃತಗೊಳಿಸುವ ಐಕಾನ್"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ಅಥವಾ"</string>
     <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"ಡ್ರ್ಯಾಗ್‌ ಹ್ಯಾಂಡಲ್‌"</string>
-    <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) -->
-    <skip />
+    <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"ಕೀಬೋರ್ಡ್ ಸೆಟ್ಟಿಂಗ್‌ಗಳು"</string>
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"ನಿಮ್ಮ ಕೀಬೋರ್ಡ್ ಬಳಸಿ ನ್ಯಾವಿಗೇಟ್ ಮಾಡಿ"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"ಕೀಬೋರ್ಡ್ ಶಾರ್ಟ್‌ಕಟ್‌ಗಳನ್ನು ಕಲಿಯಿರಿ"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"ನಿಮ್ಮ ಟಚ್‌ಪ್ಯಾಡ್ ಬಳಸಿ ನ್ಯಾವಿಗೇಟ್ ಮಾಡಿ"</string>
@@ -1464,8 +1477,6 @@
     <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"ಆ್ಯಪ್‌ಗಳಿಂದ ಒದಗಿಸಲಾಗಿದೆ"</string>
     <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"ಡಿಸ್‌ಪ್ಲೇ"</string>
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"ಅಪರಿಚಿತ"</string>
-    <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) -->
-    <skip />
-    <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) -->
-    <skip />
+    <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"ಟೈಲ್‌ಗಳನ್ನು ರೀಸೆಟ್ ಮಾಡಿ"</string>
+    <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"ಟೈಲ್‌ಗಳನ್ನು ಅವುಗಳ ಮೂಲ ಆರ್ಡರ್ ಮತ್ತು ಗಾತ್ರಗಳಿಗೆ ರೀಸೆಟ್ ಮಾಡಬೇಕೇ?"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml
index 0c1dc01e..8674dc3 100644
--- a/packages/SystemUI/res/values-ko/strings.xml
+++ b/packages/SystemUI/res/values-ko/strings.xml
@@ -136,11 +136,17 @@
     <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"현재 <xliff:g id="APP_NAME">%1$s</xliff:g>의 콘텐츠를 녹화 중입니다."</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"녹화 중지"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"화면 공유 중"</string>
+    <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
+    <skip />
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"화면 공유를 중지하시겠습니까?"</string>
+    <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
+    <skip />
     <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"현재 전체 화면을 <xliff:g id="HOST_APP_NAME">%1$s</xliff:g> 앱과 공유 중입니다."</string>
     <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"현재 전체 화면을 앱과 공유 중입니다"</string>
     <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"현재 <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>의 콘텐츠를 공유 중입니다."</string>
     <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"현재 앱을 공유 중입니다"</string>
+    <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
+    <skip />
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"공유 중지"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"화면 전송 중"</string>
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"전송을 중지할까요?"</string>
@@ -515,6 +521,10 @@
     <string name="communal_widget_picker_title" msgid="1953369090475731663">"잠금 화면 위젯"</string>
     <string name="communal_widget_picker_description" msgid="490515450110487871">"태블릿이 잠겨 있어도 누구나 잠금 화면에서 위젯을 볼 수 있습니다."</string>
     <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"위젯 선택 해제"</string>
+    <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
+    <skip />
+    <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
+    <skip />
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"잠금 화면 위젯"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"위젯을 사용하여 앱을 열려면 본인 인증을 해야 합니다. 또한 태블릿이 잠겨 있더라도 누구나 볼 수 있다는 점을 유의해야 합니다. 일부 위젯은 잠금 화면에 적합하지 않고 여기에 추가하기에 안전하지 않을 수 있습니다."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"확인"</string>
@@ -553,8 +563,8 @@
     <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"화면을 전송하시겠습니까?"</string>
     <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"앱 1개 전송"</string>
     <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"전체 화면 전송"</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"전체 화면을 전송하면 화면이 있는 모든 항목이 표시됩니다. 따라서 비밀번호, 결제 세부정보, 메시지, 사진, 오디오 및 동영상 등이 노출되지 않도록 주의하세요."</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"앱을 전송하면 해당 앱에 표시되거나 재생되는 모든 항목이 표시됩니다. 따라서 비밀번호, 결제 세부정보, 메시지, 사진, 오디오 및 동영상 등이 노출되지 않도록 주의하세요."</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"전체 화면을 전송하면 화면에 있는 모든 항목을 볼 수 있게 됩니다. 따라서 비밀번호, 결제 세부정보, 메시지, 사진, 오디오 및 동영상 등이 노출되지 않도록 주의하세요."</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"앱을 전송하면 해당 앱에 표시되거나 재생되는 모든 항목을 볼 수 있게 됩니다. 따라서 비밀번호, 결제 세부정보, 메시지, 사진, 오디오 및 동영상 등이 노출되지 않도록 주의하세요."</string>
     <string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"화면 전송"</string>
     <string name="media_projection_entry_cast_app_selector_title" msgid="6323062146661922387">"전송할 앱 선택"</string>
     <string name="media_projection_entry_generic_permission_dialog_title" msgid="4519802931547483628">"공유를 시작하시겠습니까?"</string>
@@ -571,6 +581,10 @@
     <string name="clear_all_notifications_text" msgid="348312370303046130">"모두 지우기"</string>
     <string name="manage_notifications_text" msgid="6885645344647733116">"관리"</string>
     <string name="manage_notifications_history_text" msgid="57055985396576230">"기록"</string>
+    <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
+    <skip />
+    <!-- no translation found for notification_history_button_description (1578657591405033383) -->
+    <skip />
     <string name="notification_section_header_incoming" msgid="850925217908095197">"새 알림"</string>
     <string name="notification_section_header_gentle" msgid="6804099527336337197">"무음"</string>
     <string name="notification_section_header_alerting" msgid="5581175033680477651">"알림"</string>
@@ -855,7 +869,7 @@
     <string name="system_multitasking_rhs" msgid="8714224917276297810">"현재 앱이 오른쪽에 오도록 화면 분할 사용"</string>
     <string name="system_multitasking_lhs" msgid="8402954791206308783">"현재 앱이 왼쪽에 오도록 화면 분할 사용"</string>
     <string name="system_multitasking_full_screen" msgid="336048080383640562">"화면 분할에서 전체 화면으로 전환"</string>
-    <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"화면 분할을 사용하는 중에 오른쪽 또는 아래쪽에 있는 앱으로 전환하기"</string>
+    <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"화면 분할을 사용하는 중에 오른쪽 또는 아래쪽에 있는 앱으로 전환"</string>
     <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"화면 분할을 사용하는 중에 왼쪽 또는 위쪽에 있는 앱으로 전환하기"</string>
     <string name="system_multitasking_replace" msgid="7410071959803642125">"화면 분할 중: 다른 앱으로 바꾸기"</string>
     <string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"입력"</string>
@@ -870,7 +884,7 @@
     <string name="keyboard_shortcut_group_applications_email" msgid="7852376788894975192">"이메일"</string>
     <string name="keyboard_shortcut_group_applications_sms" msgid="6912633831752843566">"SMS"</string>
     <string name="keyboard_shortcut_group_applications_music" msgid="9032078456666204025">"음악"</string>
-    <string name="keyboard_shortcut_group_applications_calendar" msgid="4229602992120154157">"캘린더"</string>
+    <string name="keyboard_shortcut_group_applications_calendar" msgid="4229602992120154157">"Calendar"</string>
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6316043911946540137">"계산기"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7312554713993114342">"지도"</string>
     <string name="volume_and_do_not_disturb" msgid="502044092739382832">"방해 금지 모드"</string>
@@ -1395,7 +1409,7 @@
     <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"최근 앱"</string>
     <string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"화면 분할"</string>
     <string name="shortcut_helper_category_input" msgid="8674018654124839566">"입력"</string>
-    <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"앱 바로가기"</string>
+    <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"앱 단축키"</string>
     <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"현재 앱"</string>
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"접근성"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"단축키"</string>
@@ -1405,8 +1419,7 @@
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"확장 아이콘"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"또는"</string>
     <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"드래그 핸들"</string>
-    <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) -->
-    <skip />
+    <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"키보드 설정"</string>
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"키보드를 사용하여 이동"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"단축키 알아보기"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"터치패드를 사용하여 이동"</string>
@@ -1464,8 +1477,6 @@
     <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"앱에서 제공"</string>
     <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"디스플레이"</string>
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"알 수 없음"</string>
-    <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) -->
-    <skip />
-    <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) -->
-    <skip />
+    <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"타일 재설정"</string>
+    <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"타일을 원래 순서 및 크기로 재설정하시겠습니까?"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml
index b74e494..363342a 100644
--- a/packages/SystemUI/res/values-ky/strings.xml
+++ b/packages/SystemUI/res/values-ky/strings.xml
@@ -136,11 +136,17 @@
     <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Учурда <xliff:g id="APP_NAME">%1$s</xliff:g> колдонмосун жаздырып жатасыз"</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Жаздырууну токтотуу"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Экран бөлүшүлүүдө"</string>
+    <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
+    <skip />
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Экранды бөлүшүүнү токтотосузбу?"</string>
+    <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
+    <skip />
     <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Учурда бүтүндөй экраныңызды <xliff:g id="HOST_APP_NAME">%1$s</xliff:g> менен бөлүшүп жатасыз"</string>
     <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Учурда бүтүндөй экраныңызды колдонмо менен бөлүшүп жатасыз"</string>
     <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"Учурда <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> колдонмосун бөлүшүп жатасыз"</string>
     <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"Учурда колдонмону бөлүшүп жатасыз"</string>
+    <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
+    <skip />
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Бөлүшүүнү токтотуу"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Тышкы экранга чыгарылууда"</string>
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Тышкы экранга чыгарууну токтотосузбу?"</string>
@@ -515,6 +521,10 @@
     <string name="communal_widget_picker_title" msgid="1953369090475731663">"Кулпуланган экрандагы виджеттер"</string>
     <string name="communal_widget_picker_description" msgid="490515450110487871">"Кулпуланган планшетте баарына көрүнүп турат."</string>
     <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"виджетти тандоодон чыгаруу"</string>
+    <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
+    <skip />
+    <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
+    <skip />
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Кулпуланган экрандагы виджеттер"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Колдонмону виджет аркылуу ачуу үчүн өзүңүздү ырасташыңыз керек. Алар кулпуланган планшетиңизде да көрүнүп турат. Кээ бир виджеттерди кулпуланган экранда колдоно албайсыз, андыктан аларды ал жерге кошпой эле койгонуңуз оң."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Түшүндүм"</string>
@@ -550,11 +560,11 @@
     <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Экранды бөлүшүү"</string>
     <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> бул параметрди өчүрүп койду"</string>
     <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Бөлүшүү үчүн колдонмо тандоо"</string>
-    <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Экранды тышкы экранга чыгарасызбы?"</string>
-    <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Бир колдонмону тышкы экранга чыгаруу"</string>
-    <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"Бүтүндөй экранды тышкы экранга чыгаруу"</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"Бүтүндөй экранды тышкы экранга чыгарганда андагы бардык нерселер көрүнөт. Андыктан сырсөздөр, төлөмдүн чоо-жайы, билдирүүлөр, сүрөттөр, аудио жана видео сыяктуу нерселерди көрсөтүп албаңыз."</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"Колдонмону тышкы экранга чыгарганда ал колдонмодо көрсөтүлүп же ойнотулуп жаткан нерселер көрүнөт. Андыктан сырсөздөр, төлөмдүн чоо-жайы, билдирүүлөр, сүрөттөр, аудио жана видео сыяктуу нерселерди көрсөтүп албаңыз."</string>
+    <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Экранды башка түзмөккө чыгарасызбы?"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Бир колдонмону чыгаруу"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"Бүтүндөй экранды чыгаруу"</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"Бүтүндөй экранды тышкы экранга чыгарганда, андагы нерселердин баары көрүнөт. Андыктан сырсөздөр, төлөмдүн чоо-жайы, билдирүүлөр, сүрөттөр, аудио жана видео сыяктуу нерселер менен этият болуңуз."</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"Колдонмону тышкы экранга чыгарганда, андагы нерселердин баары көрүнөт. Андыктан сырсөздөр, төлөмдүн чоо-жайы, билдирүүлөр, сүрөттөр, аудио жана видео сыяктуу нерселер менен этият болуңуз."</string>
     <string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"Тышкы экранга чыгаруу"</string>
     <string name="media_projection_entry_cast_app_selector_title" msgid="6323062146661922387">"Тышкы экранга чыгаруу үчүн колдонмо тандоо"</string>
     <string name="media_projection_entry_generic_permission_dialog_title" msgid="4519802931547483628">"Бөлүшүү башталсынбы?"</string>
@@ -571,6 +581,10 @@
     <string name="clear_all_notifications_text" msgid="348312370303046130">"Баарын тазалап салуу"</string>
     <string name="manage_notifications_text" msgid="6885645344647733116">"Башкаруу"</string>
     <string name="manage_notifications_history_text" msgid="57055985396576230">"Таржымал"</string>
+    <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
+    <skip />
+    <!-- no translation found for notification_history_button_description (1578657591405033383) -->
+    <skip />
     <string name="notification_section_header_incoming" msgid="850925217908095197">"Жаңы"</string>
     <string name="notification_section_header_gentle" msgid="6804099527336337197">"Үнсүз"</string>
     <string name="notification_section_header_alerting" msgid="5581175033680477651">"Билдирмелер"</string>
@@ -800,7 +814,7 @@
     <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string>
     <string name="keyboard_key_space" msgid="6980847564173394012">"Боштук"</string>
     <string name="keyboard_key_enter" msgid="8633362970109751646">"Enter"</string>
-    <string name="keyboard_key_backspace" msgid="4095278312039628074">"Артка өчүрүү"</string>
+    <string name="keyboard_key_backspace" msgid="4095278312039628074">"Backspace"</string>
     <string name="keyboard_key_media_play_pause" msgid="8389984232732277478">"Ойнотуу/Тындыруу"</string>
     <string name="keyboard_key_media_stop" msgid="1509943745250377699">"Токтотуу"</string>
     <string name="keyboard_key_media_next" msgid="8502476691227914952">"Кийинки"</string>
@@ -855,7 +869,7 @@
     <string name="system_multitasking_rhs" msgid="8714224917276297810">"Учурдагы колдонмону оңго жылдырып, экранды бөлүү"</string>
     <string name="system_multitasking_lhs" msgid="8402954791206308783">"Учурдагы колдонмону солго жылдырып, экранды бөлүү"</string>
     <string name="system_multitasking_full_screen" msgid="336048080383640562">"Экранды бөлүү режиминен толук экранга которулуу"</string>
-    <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Бөлүнгөн экранды колдонуп жатканда сол же төмөн жактагы колдонмого которулуңуз"</string>
+    <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Бөлүнгөн экранда сол же төмөн жактагы колдонмого которулуу"</string>
     <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Бөлүнгөн экранды колдонуп жатканда сол же жогору жактагы колдонмого которулуңуз"</string>
     <string name="system_multitasking_replace" msgid="7410071959803642125">"Экранды бөлүү режиминде бир колдонмону экинчисине алмаштыруу"</string>
     <string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Киргизүү"</string>
@@ -1405,11 +1419,10 @@
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Жайып көрсөтүү сүрөтчөсү"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"же"</string>
     <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Cүйрөө маркери"</string>
-    <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) -->
-    <skip />
+    <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Баскычтоп параметрлери"</string>
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Нерселерге баскычтоп аркылуу өтүңүз"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Ыкчам баскычтар тууралуу билип алыңыз"</string>
-    <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Нерселерге сенсордук такта аркылуу өтүңүз"</string>
+    <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Керектүү жерге сенсордук такта аркылуу өтөсүз"</string>
     <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Сенсордук тактадагы жаңсоолорду үйрөнүп алыңыз"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Нерселерге баскычтоп жана сенсордук такта аркылуу өтүңүз"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Сенсордук тактадагы жаңсоолор, ыкчам баскычтар жана башкалар жөнүндө билип алыңыз"</string>
@@ -1464,8 +1477,6 @@
     <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Колдонмолор сунуштады"</string>
     <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Экран"</string>
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Белгисиз"</string>
-    <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) -->
-    <skip />
-    <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) -->
-    <skip />
+    <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"Карталарды баштапкы абалга келтирүү"</string>
+    <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"Карталар баштапкы иретине жана өлчөмдөрүнө кайтарылсынбы?"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml
index ce8c959..54c2c6b 100644
--- a/packages/SystemUI/res/values-lo/strings.xml
+++ b/packages/SystemUI/res/values-lo/strings.xml
@@ -136,11 +136,17 @@
     <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"ທ່ານກຳລັງບັນທຶກ <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"ຢຸດການບັນທຶກ"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"ກຳລັງແບ່ງປັນໜ້າຈໍ"</string>
+    <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
+    <skip />
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"ຢຸດການແບ່ງປັນໜ້າຈໍບໍ?"</string>
+    <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
+    <skip />
     <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"ທ່ານກຳລັງແບ່ງປັນທັງໝົດໜ້າຈໍຂອງທ່ານກັບ <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>"</string>
     <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"ທ່ານກຳລັງແບ່ງປັນທັງໝົດໜ້າຈໍຂອງທ່ານກັບແອັບ"</string>
     <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"ທ່ານກຳລັງແບ່ງປັນ <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>"</string>
     <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"ທ່ານກຳລັງແບ່ງປັນແອັບ"</string>
+    <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
+    <skip />
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"ຢຸດການແບ່ງປັນ"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"ກຳລັງສົ່ງສັນຍານໜ້າຈໍ"</string>
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"ຢຸດການສົ່ງສັນຍານບໍ?"</string>
@@ -515,6 +521,10 @@
     <string name="communal_widget_picker_title" msgid="1953369090475731663">"ວິດເຈັດໃນໜ້າຈໍລັອກ"</string>
     <string name="communal_widget_picker_description" msgid="490515450110487871">"ທຸກຄົນສາມາດເບິ່ງວິດເຈັດຢູ່ໜ້າຈໍລັອກຂອງທ່ານໄດ້, ເຖິງແມ່ນວ່າແທັບເລັດຂອງທ່ານຈະລັອກຢູ່ກໍຕາມ."</string>
     <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"ຍົກເລີກການເລືອກວິດເຈັດ"</string>
+    <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
+    <skip />
+    <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
+    <skip />
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"ວິດເຈັດໃນໜ້າຈໍລັອກ"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"ເພື່ອເປີດແອັບໂດຍໃຊ້ວິດເຈັດ, ທ່ານຈະຕ້ອງຢັ້ງຢືນວ່າແມ່ນທ່ານ. ນອກຈາກນັ້ນ, ກະລຸນາຮັບຊາບວ່າທຸກຄົນສາມາດເບິ່ງຂໍ້ມູນດັ່ງກ່າວໄດ້, ເຖິງແມ່ນວ່າແທັບເລັດຂອງທ່ານຈະລັອກຢູ່ກໍຕາມ. ວິດເຈັດບາງຢ່າງອາດບໍ່ໄດ້ມີໄວ້ສຳລັບໜ້າຈໍລັອກຂອງທ່ານ ແລະ ອາດບໍ່ປອດໄພທີ່ຈະເພີ່ມໃສ່ບ່ອນນີ້."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"ເຂົ້າໃຈແລ້ວ"</string>
@@ -571,6 +581,10 @@
     <string name="clear_all_notifications_text" msgid="348312370303046130">"ລຶບລ້າງທັງໝົດ"</string>
     <string name="manage_notifications_text" msgid="6885645344647733116">"ຈັດການ"</string>
     <string name="manage_notifications_history_text" msgid="57055985396576230">"ປະຫວັດ"</string>
+    <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
+    <skip />
+    <!-- no translation found for notification_history_button_description (1578657591405033383) -->
+    <skip />
     <string name="notification_section_header_incoming" msgid="850925217908095197">"ໃໝ່"</string>
     <string name="notification_section_header_gentle" msgid="6804099527336337197">"ປິດສຽງ"</string>
     <string name="notification_section_header_alerting" msgid="5581175033680477651">"ການແຈ້ງເຕືອນ"</string>
@@ -1405,8 +1419,7 @@
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"ໄອຄອນຂະຫຍາຍ"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ຫຼື"</string>
     <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"ບ່ອນຈັບລາກ"</string>
-    <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) -->
-    <skip />
+    <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"ການຕັ້ງຄ່າແປ້ນພິມ"</string>
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"ນຳທາງໂດຍໃຊ້ແປ້ນພິມຂອງທ່ານ"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"ສຶກສາຄີລັດ"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"ນຳທາງໂດຍໃຊ້ແຜ່ນສຳຜັດຂອງທ່ານ"</string>
@@ -1464,8 +1477,6 @@
     <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"ສະໜອງໃຫ້ໂດຍແອັບ"</string>
     <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"ການສະແດງຜົນ"</string>
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"ບໍ່ຮູ້ຈັກ"</string>
-    <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) -->
-    <skip />
-    <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) -->
-    <skip />
+    <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"ຣີເຊັດແຜ່ນ"</string>
+    <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"ຣີເຊັດແຜ່ນເປັນການຈັດຮຽງ ແລະ ຂະໜາດເດີມບໍ?"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml
index a7906b6..dca26a4 100644
--- a/packages/SystemUI/res/values-lt/strings.xml
+++ b/packages/SystemUI/res/values-lt/strings.xml
@@ -136,11 +136,17 @@
     <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Šiuo metu įrašote šią programą: <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Sustabdyti įrašymą"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Bendrinamas ekranas"</string>
+    <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
+    <skip />
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Nebebendrinti ekrano?"</string>
+    <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
+    <skip />
     <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Šiuo metu bendrinate visą ekraną su šia programa: <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>"</string>
     <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Šiuo metu bendrinate visą ekraną su programa"</string>
     <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"Šiuo metu bendrinate šią programą: <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>"</string>
     <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"Šiuo metu bendrinate programą"</string>
+    <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
+    <skip />
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Nebebendrinti"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Perduodamas ekranas"</string>
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Sustabdyti perdavimą?"</string>
@@ -515,6 +521,10 @@
     <string name="communal_widget_picker_title" msgid="1953369090475731663">"Užrakinimo ekrano valdikliai"</string>
     <string name="communal_widget_picker_description" msgid="490515450110487871">"Visi gali žr. valdiklius užrakinimo ekrane, net užrakinus planšetinį kompiuterį."</string>
     <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"atšaukti valdiklio pasirinkimą"</string>
+    <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
+    <skip />
+    <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
+    <skip />
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Užrakinimo ekrano valdikliai"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Kad galėtumėte atidaryti programą naudodami valdiklį, turėsite patvirtinti savo tapatybę. Be to, atminkite, kad bet kas gali peržiūrėti valdiklius net tada, kai planšetinis kompiuteris užrakintas. Kai kurie valdikliai gali būti neskirti jūsų užrakinimo ekranui ir gali būti nesaugu juos čia pridėti."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Supratau"</string>
@@ -571,6 +581,10 @@
     <string name="clear_all_notifications_text" msgid="348312370303046130">"Viską išvalyti"</string>
     <string name="manage_notifications_text" msgid="6885645344647733116">"Tvarkyti"</string>
     <string name="manage_notifications_history_text" msgid="57055985396576230">"Istorija"</string>
+    <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
+    <skip />
+    <!-- no translation found for notification_history_button_description (1578657591405033383) -->
+    <skip />
     <string name="notification_section_header_incoming" msgid="850925217908095197">"Nauja"</string>
     <string name="notification_section_header_gentle" msgid="6804099527336337197">"Tylus"</string>
     <string name="notification_section_header_alerting" msgid="5581175033680477651">"Pranešimai"</string>
@@ -1405,8 +1419,7 @@
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Išskleidimo piktograma"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"arba"</string>
     <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Vilkimo rankenėlė"</string>
-    <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) -->
-    <skip />
+    <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Klaviatūros nustatymai"</string>
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Naršykite naudodamiesi klaviatūra"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Sužinokite apie sparčiuosius klavišus"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Naršykite naudodamiesi jutikline dalimi"</string>
diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml
index cff7278..c5ba0ad 100644
--- a/packages/SystemUI/res/values-lv/strings.xml
+++ b/packages/SystemUI/res/values-lv/strings.xml
@@ -136,11 +136,17 @@
     <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Pašlaik ierakstāt lietotni <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Apturēt ierakstīšanu"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Notiek ekrāna kopīgošana"</string>
+    <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
+    <skip />
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Vai apturēt ekrāna kopīgošanu?"</string>
+    <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
+    <skip />
     <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Pašlaik kopīgojat visu ekrānu ar lietotni <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>"</string>
     <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Pašlaik kopīgojat visu ekrānu ar lietotni"</string>
     <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"Pašlaik kopīgojat lietotni <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>"</string>
     <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"Pašlaik kopīgojat lietotni"</string>
+    <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
+    <skip />
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Apturēt kopīgošanu"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Notiek ekrāna apraide"</string>
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Vai pārtraukt apraidi?"</string>
@@ -515,6 +521,10 @@
     <string name="communal_widget_picker_title" msgid="1953369090475731663">"Bloķēšanas ekrāna logrīki"</string>
     <string name="communal_widget_picker_description" msgid="490515450110487871">"Jebkurš var skatīt logrīkus bloķēšanas ekrānā, pat ja planšetdators ir bloķēts."</string>
     <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"noņemt logrīka atlasi"</string>
+    <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
+    <skip />
+    <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
+    <skip />
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Bloķēšanas ekrāna logrīki"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Lai atvērtu lietotni, izmantojot logrīku, jums būs jāapstiprina sava identitāte. Turklāt ņemiet vērā, ka ikviens var skatīt logrīkus, pat ja planšetdators ir bloķēts. Iespējams, daži logrīki nav paredzēti izmantošanai bloķēšanas ekrānā, un var nebūt droši tos šeit pievienot."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Labi"</string>
@@ -571,6 +581,10 @@
     <string name="clear_all_notifications_text" msgid="348312370303046130">"Dzēst visu"</string>
     <string name="manage_notifications_text" msgid="6885645344647733116">"Pārvaldīt"</string>
     <string name="manage_notifications_history_text" msgid="57055985396576230">"Vēsture"</string>
+    <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
+    <skip />
+    <!-- no translation found for notification_history_button_description (1578657591405033383) -->
+    <skip />
     <string name="notification_section_header_incoming" msgid="850925217908095197">"Jauni"</string>
     <string name="notification_section_header_gentle" msgid="6804099527336337197">"Klusums"</string>
     <string name="notification_section_header_alerting" msgid="5581175033680477651">"Paziņojumi"</string>
@@ -1405,8 +1419,7 @@
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Izvēršanas ikona"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"vai"</string>
     <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Vilkšanas turis"</string>
-    <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) -->
-    <skip />
+    <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Tastatūras iestatījumi"</string>
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Pārvietošanās, izmantojot tastatūru"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Uzziniet par īsinājumtaustiņiem."</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Pārvietošanās, izmantojot skārienpaliktni"</string>
@@ -1464,8 +1477,6 @@
     <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Nodrošina lietotnes"</string>
     <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Displejs"</string>
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Nezināma"</string>
-    <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) -->
-    <skip />
-    <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) -->
-    <skip />
+    <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"Elementu atiestatīšana"</string>
+    <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"Vai atiestatīt elementus, atjaunojot to sākotnējo secību un izmērus?"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml
index 464ef33..65e958e 100644
--- a/packages/SystemUI/res/values-mk/strings.xml
+++ b/packages/SystemUI/res/values-mk/strings.xml
@@ -136,11 +136,17 @@
     <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Во моментов ја снимате <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Сопри го снимањето"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Се споделува екранот"</string>
+    <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
+    <skip />
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Да се сопре споделувањето на екранот?"</string>
+    <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
+    <skip />
     <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Во моментов го споделувате целиот екран со <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>"</string>
     <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Во моментов го споделувате целиот екран со апликација"</string>
     <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"Во моментов ја споделувате <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>"</string>
     <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"Во моментов споделувате апликација"</string>
+    <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
+    <skip />
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Сопри го споделувањето"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Се емитува екранот"</string>
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Да се сопре емитувањето?"</string>
@@ -515,6 +521,10 @@
     <string name="communal_widget_picker_title" msgid="1953369090475731663">"Виџети на заклучен екран"</string>
     <string name="communal_widget_picker_description" msgid="490515450110487871">"Секој може да гледа виџети на заклучениот екран, дури и ако таблетот е заклучен."</string>
     <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"поништи го изборот на виџетот"</string>
+    <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
+    <skip />
+    <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
+    <skip />
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Виџети на заклучен екран"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"За да отворите апликација со помош на виџет, ќе треба да потврдите дека сте вие. Покрај тоа, имајте предвид дека секој може да ги гледа виџетите, дури и кога вашиот таблет е заклучен. Некои виџети можеби не се наменети за вашиот заклучен екран, па можеби не е безбедно да се додадат овде."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Сфатив"</string>
@@ -571,6 +581,10 @@
     <string name="clear_all_notifications_text" msgid="348312370303046130">"Избриши сѐ"</string>
     <string name="manage_notifications_text" msgid="6885645344647733116">"Управувајте"</string>
     <string name="manage_notifications_history_text" msgid="57055985396576230">"Историја"</string>
+    <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
+    <skip />
+    <!-- no translation found for notification_history_button_description (1578657591405033383) -->
+    <skip />
     <string name="notification_section_header_incoming" msgid="850925217908095197">"Нов"</string>
     <string name="notification_section_header_gentle" msgid="6804099527336337197">"Безгласно"</string>
     <string name="notification_section_header_alerting" msgid="5581175033680477651">"Известувања"</string>
@@ -840,7 +854,7 @@
     <string name="keyboard_shortcut_a11y_filter_current_app" msgid="7944592357493737911">"Се прикажуваат кратенки за тековната апликација"</string>
     <string name="group_system_access_notification_shade" msgid="1619028907006553677">"Прегледајте ги известувањата"</string>
     <string name="group_system_full_screenshot" msgid="5742204844232667785">"Направете слика од екранот"</string>
-    <string name="group_system_access_system_app_shortcuts" msgid="8562482996626694026">"Прикажи кратенки"</string>
+    <string name="group_system_access_system_app_shortcuts" msgid="8562482996626694026">"Прикажете кратенки"</string>
     <string name="group_system_go_back" msgid="2730322046244918816">"Вратете се назад"</string>
     <string name="group_system_access_home_screen" msgid="4130366993484706483">"Одете на почетниот екран"</string>
     <string name="group_system_overview_open_apps" msgid="5659958952937994104">"Прегледајте ги неодамнешните апликации"</string>
@@ -1405,8 +1419,7 @@
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Икона за проширување"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"или"</string>
     <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Рачка за влечење"</string>
-    <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) -->
-    <skip />
+    <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Поставки за тастатурата"</string>
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Движете се со користење на тастатурата"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Научете кратенки од тастатурата"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Движете се со користење на допирната подлога"</string>
@@ -1464,8 +1477,6 @@
     <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Обезбедено од апликации"</string>
     <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Екран"</string>
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Непознато"</string>
-    <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) -->
-    <skip />
-    <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) -->
-    <skip />
+    <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"Ресетирајте ги плочките"</string>
+    <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"Да се ресетираат плочките на нивниот првичен редослед и големини?"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml
index 08e9799..e10ecff 100644
--- a/packages/SystemUI/res/values-ml/strings.xml
+++ b/packages/SystemUI/res/values-ml/strings.xml
@@ -136,11 +136,17 @@
     <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"നിങ്ങൾ ഇപ്പോൾ <xliff:g id="APP_NAME">%1$s</xliff:g> റെക്കോർഡ് ചെയ്യുകയാണ്"</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"റെക്കോർഡിംഗ് നിർത്തുക"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"സ്‌ക്രീൻ പങ്കിടുന്നു"</string>
+    <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
+    <skip />
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"സ്‌ക്രീൻ പങ്കിടുന്നത് നിർത്തണോ?"</string>
+    <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
+    <skip />
     <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"നിങ്ങൾ ഇപ്പോൾ മുഴുവൻ സ്ക്രീനും <xliff:g id="HOST_APP_NAME">%1$s</xliff:g> എന്നതുമായി പങ്കിടുകയാണ്"</string>
     <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"നിങ്ങൾ ഇപ്പോൾ മുഴുവൻ സ്ക്രീനും ഒരു ആപ്പുമായി പങ്കിടുകയാണ്"</string>
     <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"നിങ്ങൾ ഇപ്പോൾ <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> പങ്കിടുകയാണ്"</string>
     <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"നിങ്ങൾ നിലവിൽ ഒരു ആപ്പ് പങ്കിടുകയാണ്"</string>
+    <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
+    <skip />
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"പങ്കിടൽ നിർത്തുക"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"സ്‌ക്രീൻ കാസ്‌റ്റ് ചെയ്യുന്നു"</string>
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"കാസ്റ്റ് ചെയ്യുന്നത് അവസാനിപ്പിക്കണോ?"</string>
@@ -515,6 +521,10 @@
     <string name="communal_widget_picker_title" msgid="1953369090475731663">"ലോക്ക് സ്‌ക്രീൻ വിജറ്റുകൾ"</string>
     <string name="communal_widget_picker_description" msgid="490515450110487871">"ടാബ്‌ലെറ്റ് ലോക്കാണെങ്കിൽ പോലും ലോക്ക് സ്ക്രീനിൽ ആർക്കും വിജറ്റുകൾ കാണാം."</string>
     <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"വിജറ്റ് തിരഞ്ഞെടുത്തത് മാറ്റുക"</string>
+    <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
+    <skip />
+    <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
+    <skip />
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"ലോക്ക് സ്‌ക്രീൻ വിജറ്റുകൾ"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"വിജറ്റ് ഉപയോഗിച്ച് ഒരു ആപ്പ് തുറക്കാൻ, ഇത് നിങ്ങൾ തന്നെയാണെന്ന് പരിശോധിച്ചുറപ്പിക്കേണ്ടതുണ്ട്. നിങ്ങളുടെ ടാബ്‌ലെറ്റ് ലോക്കായിരിക്കുമ്പോഴും എല്ലാവർക്കും അത് കാണാനാകുമെന്നതും ഓർക്കുക. ചില വിജറ്റുകൾ നിങ്ങളുടെ ലോക്ക് സ്‌ക്രീനിന് ഉള്ളതായിരിക്കില്ല, അവ ഇവിടെ ചേർക്കുന്നത് സുരക്ഷിതവുമായിരിക്കില്ല."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"മനസ്സിലായി"</string>
@@ -571,6 +581,10 @@
     <string name="clear_all_notifications_text" msgid="348312370303046130">"എല്ലാം മായ്‌ക്കുക"</string>
     <string name="manage_notifications_text" msgid="6885645344647733116">"മാനേജ് ചെയ്യുക"</string>
     <string name="manage_notifications_history_text" msgid="57055985396576230">"ചരിത്രം"</string>
+    <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
+    <skip />
+    <!-- no translation found for notification_history_button_description (1578657591405033383) -->
+    <skip />
     <string name="notification_section_header_incoming" msgid="850925217908095197">"പുതിയത്"</string>
     <string name="notification_section_header_gentle" msgid="6804099527336337197">"നിശബ്‌ദം"</string>
     <string name="notification_section_header_alerting" msgid="5581175033680477651">"അറിയിപ്പുകൾ"</string>
@@ -1405,8 +1419,7 @@
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"വികസിപ്പിക്കൽ ഐക്കൺ"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"അല്ലെങ്കിൽ"</string>
     <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"വലിച്ചിടുന്നതിനുള്ള ഹാൻഡിൽ"</string>
-    <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) -->
-    <skip />
+    <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"കീബോർഡ് ക്രമീകരണം"</string>
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"നിങ്ങളുടെ കീബോർഡ് ഉപയോഗിച്ച് നാവിഗേറ്റ് ചെയ്യുക"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"കീബോർഡ് കുറുക്കുവഴികൾ മനസ്സിലാക്കുക"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"നിങ്ങളുടെ ടച്ച്‌പാഡ് ഉപയോഗിച്ച് നാവിഗേറ്റ് ചെയ്യുക"</string>
@@ -1464,8 +1477,6 @@
     <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"ആപ്പുകൾ നൽകുന്നത്"</string>
     <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"ഡിസ്‌പ്ലേ"</string>
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"അജ്ഞാതം"</string>
-    <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) -->
-    <skip />
-    <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) -->
-    <skip />
+    <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"ടൈലുകൾ റീസെറ്റ് ചെയ്യുക"</string>
+    <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"ടൈലുകൾ അവയുടെ ഒറിജിനൽ ക്രമത്തിലേക്കും വലുപ്പങ്ങളിലേക്കും റീസെറ്റ് ചെയ്യണോ?"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml
index 73b6aab..cb5f054 100644
--- a/packages/SystemUI/res/values-mn/strings.xml
+++ b/packages/SystemUI/res/values-mn/strings.xml
@@ -136,11 +136,17 @@
     <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Та одоогоор <xliff:g id="APP_NAME">%1$s</xliff:g>-г бичиж байна"</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Бичихийг зогсоох"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Дэлгэцийг хуваалцаж байна"</string>
+    <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
+    <skip />
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Дэлгэц хуваалцахыг зогсоох уу?"</string>
+    <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
+    <skip />
     <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Та одоогоор дэлгэцээ бүтнээр нь <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>-тай хуваалцаж байна"</string>
     <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Та одоогоор дэлгэцээ бүтнээр нь нэг апптай хуваалцаж байна"</string>
     <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"Та одоогоор <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>-г хуваалцаж байна"</string>
     <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"Та одоогоор нэг аппыг хуваалцаж байна"</string>
+    <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
+    <skip />
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Хуваалцахыг зогсоох"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Дэлгэцийг дамжуулж байна"</string>
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Дамжуулахaa болих уу?"</string>
@@ -515,6 +521,10 @@
     <string name="communal_widget_picker_title" msgid="1953369090475731663">"Түгжээтэй дэлгэцийн виджет"</string>
     <string name="communal_widget_picker_description" msgid="490515450110487871">"Таны таблет түгжээтэй байсан ч түгжээтэй дэлгэцийн виджетийг тань дурын хүн үзнэ"</string>
     <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"виджетийн сонголтыг болиулах"</string>
+    <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
+    <skip />
+    <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
+    <skip />
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Түгжээтэй дэлгэцийн виджет"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Виджет ашиглан аппыг нээхийн тулд та өөрийгөө мөн болохыг баталгаажуулах шаардлагатай болно. Мөн таны таблет түгжээтэй байсан ч тэдгээрийг дурын хүн үзэж болохыг санаарай. Зарим виджет таны түгжээтэй дэлгэцэд зориулагдаагүй байж магадгүй ба энд нэмэхэд аюултай байж болзошгүй."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Ойлголоо"</string>
@@ -571,6 +581,10 @@
     <string name="clear_all_notifications_text" msgid="348312370303046130">"Бүгдийг арилгах"</string>
     <string name="manage_notifications_text" msgid="6885645344647733116">"Удирдах"</string>
     <string name="manage_notifications_history_text" msgid="57055985396576230">"Түүх"</string>
+    <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
+    <skip />
+    <!-- no translation found for notification_history_button_description (1578657591405033383) -->
+    <skip />
     <string name="notification_section_header_incoming" msgid="850925217908095197">"Шинэ"</string>
     <string name="notification_section_header_gentle" msgid="6804099527336337197">"Чимээгүй"</string>
     <string name="notification_section_header_alerting" msgid="5581175033680477651">"Мэдэгдлүүд"</string>
@@ -1405,8 +1419,7 @@
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Дэлгэх дүрс тэмдэг"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"эсвэл"</string>
     <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Чирэх бариул"</string>
-    <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) -->
-    <skip />
+    <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Гарын тохиргоо"</string>
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Гараа ашиглан шилжих"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Товчлуурын шууд холбоосыг мэдэж аваарай"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Мэдрэгч самбараа ашиглан шилжээрэй"</string>
@@ -1464,8 +1477,6 @@
     <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Аппуудаас өгсөн"</string>
     <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Дэлгэц"</string>
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Тодорхойгүй"</string>
-    <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) -->
-    <skip />
-    <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) -->
-    <skip />
+    <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"Хавтангуудыг шинэчлэх"</string>
+    <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"Хавтангуудыг эх дараалал, хэмжээ рүү нь шинэчлэх үү?"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml
index 22044c2..8aa0afa 100644
--- a/packages/SystemUI/res/values-mr/strings.xml
+++ b/packages/SystemUI/res/values-mr/strings.xml
@@ -136,11 +136,17 @@
     <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"तुम्ही सध्या <xliff:g id="APP_NAME">%1$s</xliff:g> रेकॉर्ड करत आहात"</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"रेकॉर्ड करणे थांबवा"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"स्क्रीन शेअर करत आहे"</string>
+    <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
+    <skip />
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"स्क्रीन शेअर करणे थांबवायचे आहे का?"</string>
+    <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
+    <skip />
     <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"तुम्ही सध्या तुमची संपूर्ण स्क्रीन <xliff:g id="HOST_APP_NAME">%1$s</xliff:g> सह शेअर करत आहात"</string>
     <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"तुम्ही सध्या तुमची संपूर्ण स्क्रीन एका ॲपसह शेअर करत आहात"</string>
     <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"तुम्ही सध्या <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> शेअर करत आहात"</string>
     <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"तुम्ही सध्या एक ॲप शेअर करत आहात"</string>
+    <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
+    <skip />
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"शेअर करणे थांबवा"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"स्‍क्रीन कास्‍ट करत आहे"</string>
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"कास्ट करणे थांबवायचे आहे का?"</string>
@@ -515,6 +521,10 @@
     <string name="communal_widget_picker_title" msgid="1953369090475731663">"लॉक स्‍क्रीन विजेट"</string>
     <string name="communal_widget_picker_description" msgid="490515450110487871">"तुमचा टॅबलेट लॉक केला, तरी कोणीही तुमच्या लॉक स्क्रीनवरील विजेट पाहू शकतो."</string>
     <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"विजेटची निवड रद्द करा"</string>
+    <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
+    <skip />
+    <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
+    <skip />
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"लॉक स्‍क्रीन विजेट"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"विजेट वापरून अ‍ॅप उघडण्यासाठी, तुम्हाला हे तुम्हीच असल्याची पडताळणी करावी लागेल. तसेच, लक्षात ठेवा, तुमचा टॅबलेट लॉक असतानादेखील कोणीही ती पाहू शकते. काही विजेट कदाचित तुमच्या लॉक स्‍क्रीनसाठी नाहीत आणि ती इथे जोडणे असुरक्षित असू शकते."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"समजले"</string>
@@ -571,6 +581,10 @@
     <string name="clear_all_notifications_text" msgid="348312370303046130">"सर्व साफ करा"</string>
     <string name="manage_notifications_text" msgid="6885645344647733116">"व्यवस्थापित करा"</string>
     <string name="manage_notifications_history_text" msgid="57055985396576230">"इतिहास"</string>
+    <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
+    <skip />
+    <!-- no translation found for notification_history_button_description (1578657591405033383) -->
+    <skip />
     <string name="notification_section_header_incoming" msgid="850925217908095197">"नवीन"</string>
     <string name="notification_section_header_gentle" msgid="6804099527336337197">"सायलंट"</string>
     <string name="notification_section_header_alerting" msgid="5581175033680477651">"सूचना"</string>
@@ -1405,8 +1419,7 @@
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"विस्तार करा आयकन"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"किंवा"</string>
     <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"ड्रॅग हॅंडल"</string>
-    <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) -->
-    <skip />
+    <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"कीबोर्ड सेटिंग्ज"</string>
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"तुमचा कीबोर्ड वापरून नेव्हिगेट करा"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"कीबोर्ड शॉर्टकट जाणून घ्या"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"तुमचा टचपॅड वापरून नेव्हिगेट करा"</string>
@@ -1464,8 +1477,6 @@
     <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"अ‍ॅप्सद्वारे पुरवलेले"</string>
     <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"डिस्प्ले"</string>
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"अज्ञात"</string>
-    <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) -->
-    <skip />
-    <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) -->
-    <skip />
+    <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"टाइल रीसेट करा"</string>
+    <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"टाइल त्यांच्या मूळ क्रमानुसार आणि मूळ आकारांमध्ये रीसेट करायच्या आहेत का?"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml
index 7ee2dcd..d0071f4d 100644
--- a/packages/SystemUI/res/values-ms/strings.xml
+++ b/packages/SystemUI/res/values-ms/strings.xml
@@ -136,11 +136,17 @@
     <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Anda sedang merakam <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Hentikan rakaman"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Berkongsi skrin"</string>
+    <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
+    <skip />
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Hentikan perkongsian skrin?"</string>
+    <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
+    <skip />
     <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Anda sedang berkongsi seluruh skrin anda dengan <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>"</string>
     <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Anda sedang berkongsi seluruh skrin anda dengan apl"</string>
     <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"Anda sedang berkongsi <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>"</string>
     <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"Anda sedang berkongsi apl"</string>
+    <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
+    <skip />
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Hentikan perkongsian"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Menghantar skrin"</string>
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Hentikan penghantaran?"</string>
@@ -515,6 +521,10 @@
     <string name="communal_widget_picker_title" msgid="1953369090475731663">"Widget skrin kunci"</string>
     <string name="communal_widget_picker_description" msgid="490515450110487871">"Sesiapa sahaja boleh melihat widget pada skrin kunci, walaupun tablet dikunci."</string>
     <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"nyahpilih widget"</string>
+    <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
+    <skip />
+    <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
+    <skip />
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Widget skrin kunci"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Untuk membuka apl menggunakan widget, anda perlu mengesahkan identiti anda. Selain itu, perlu diingat bahawa sesiapa sahaja boleh melihat widget tersebut, walaupun semasa tablet anda dikunci. Sesetengah widget mungkin tidak sesuai untuk skrin kunci anda dan mungkin tidak selamat untuk ditambahkan di sini."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"OK"</string>
@@ -571,6 +581,10 @@
     <string name="clear_all_notifications_text" msgid="348312370303046130">"Kosongkan semua"</string>
     <string name="manage_notifications_text" msgid="6885645344647733116">"Urus"</string>
     <string name="manage_notifications_history_text" msgid="57055985396576230">"Sejarah"</string>
+    <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
+    <skip />
+    <!-- no translation found for notification_history_button_description (1578657591405033383) -->
+    <skip />
     <string name="notification_section_header_incoming" msgid="850925217908095197">"Baharu"</string>
     <string name="notification_section_header_gentle" msgid="6804099527336337197">"Senyap"</string>
     <string name="notification_section_header_alerting" msgid="5581175033680477651">"Pemberitahuan"</string>
@@ -1405,8 +1419,7 @@
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Kembangkan ikon"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"atau"</string>
     <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Pemegang seret"</string>
-    <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) -->
-    <skip />
+    <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Tetapan Papan Kekunci"</string>
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Navigasi menggunakan papan kekunci anda"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Ketahui pintasan papan kekunci"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Navigasi menggunakan pad sentuh anda"</string>
@@ -1464,8 +1477,6 @@
     <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Disediakan oleh apl"</string>
     <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Paparan"</string>
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Tidak diketahui"</string>
-    <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) -->
-    <skip />
-    <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) -->
-    <skip />
+    <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"Tetapkan semula jubin"</string>
+    <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"Tetapkan semula jubin kepada urutan dan saiz yang asal?"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml
index d3ccaf9..fd4c147 100644
--- a/packages/SystemUI/res/values-my/strings.xml
+++ b/packages/SystemUI/res/values-my/strings.xml
@@ -136,11 +136,17 @@
     <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"သင်သည် လက်ရှိတွင် <xliff:g id="APP_NAME">%1$s</xliff:g> ကို ရိုက်ကူးနေသည်"</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"ရိုက်ကူးမှု ရပ်ရန်"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"ဖန်သားပြင်ကို မျှဝေနေသည်"</string>
+    <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
+    <skip />
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"ဖန်သားပြင်မျှဝေခြင်း ရပ်မလား။"</string>
+    <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
+    <skip />
     <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"သင်သည် လက်ရှိတွင် <xliff:g id="HOST_APP_NAME">%1$s</xliff:g> ဖြင့် ဖန်သားပြင်တစ်ခုလုံးကို မျှဝေနေသည်"</string>
     <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"သင်သည် လက်ရှိတွင် အက်ပ်တစ်ခုဖြင့် ဖန်သားပြင်တစ်ခုလုံးကို မျှဝေနေသည်"</string>
     <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"သင်သည် လက်ရှိတွင် <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> ကို မျှဝေနေသည်"</string>
     <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"သင်သည် လက်ရှိတွင် အက်ပ်ကို မျှဝေနေသည်"</string>
+    <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
+    <skip />
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"မျှဝေခြင်း ရပ်ရန်"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"ဖန်သားပြင်ကို ကာစ်လုပ်နေသည်"</string>
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"ကာစ်လုပ်ခြင်းကို ရပ်လိုသလား။"</string>
@@ -515,6 +521,10 @@
     <string name="communal_widget_picker_title" msgid="1953369090475731663">"လော့ခ်မျက်နှာပြင် ဝိဂျက်များ"</string>
     <string name="communal_widget_picker_description" msgid="490515450110487871">"တက်ဘလက်လော့ခ်ချထားသော်လည်း မည်သူမဆို လော့ခ်မျက်နှာပြင်ဝိဂျက်ကို ကြည့်နိုင်သည်။"</string>
     <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"ဝိဂျက် ပြန်ဖြုတ်ရန်"</string>
+    <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
+    <skip />
+    <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
+    <skip />
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"လော့ခ်မျက်နှာပြင် ဝိဂျက်များ"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"ဝိဂျက်သုံး၍ အက်ပ်ဖွင့်ရန်အတွက် သင်ဖြစ်ကြောင်း အတည်ပြုရန်လိုသည်။ ထို့ပြင် သင့်တက်ဘလက် လော့ခ်ချထားချိန်၌ပင် မည်သူမဆို ၎င်းတို့ကို ကြည့်နိုင်ကြောင်း သတိပြုပါ။ ဝိဂျက်အချို့ကို လော့ခ်မျက်နှာပြင်အတွက် ရည်ရွယ်ထားခြင်း မရှိသဖြင့် ဤနေရာတွင် ထည့်ပါက မလုံခြုံနိုင်ပါ။"</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"နားလည်ပြီ"</string>
@@ -571,6 +581,10 @@
     <string name="clear_all_notifications_text" msgid="348312370303046130">"အားလုံးရှင်းရန်"</string>
     <string name="manage_notifications_text" msgid="6885645344647733116">"စီမံရန်"</string>
     <string name="manage_notifications_history_text" msgid="57055985396576230">"မှတ်တမ်း"</string>
+    <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
+    <skip />
+    <!-- no translation found for notification_history_button_description (1578657591405033383) -->
+    <skip />
     <string name="notification_section_header_incoming" msgid="850925217908095197">"အသစ်"</string>
     <string name="notification_section_header_gentle" msgid="6804099527336337197">"အသံတိတ်ခြင်း"</string>
     <string name="notification_section_header_alerting" msgid="5581175033680477651">"အကြောင်းကြားချက်များ"</string>
@@ -797,10 +811,10 @@
     <string name="keyboard_key_button_template" msgid="8005673627272051429">"ခလုတ် <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="keyboard_key_home" msgid="3734400625170020657">"ပင်မ"</string>
     <string name="keyboard_key_back" msgid="4185420465469481999">"နောက်သို့"</string>
-    <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string>
+    <string name="keyboard_key_tab" msgid="4592772350906496730">"တဘ်ခလုတ်"</string>
     <string name="keyboard_key_space" msgid="6980847564173394012">"Space"</string>
     <string name="keyboard_key_enter" msgid="8633362970109751646">"Enter ခလုတ်"</string>
-    <string name="keyboard_key_backspace" msgid="4095278312039628074">"နောက်ပြန်ဖျက်ပါ"</string>
+    <string name="keyboard_key_backspace" msgid="4095278312039628074">"နောက်ပြန်ခလုတ်"</string>
     <string name="keyboard_key_media_play_pause" msgid="8389984232732277478">"ဖွင့်ပါ/ခဏရပ်ပါ"</string>
     <string name="keyboard_key_media_stop" msgid="1509943745250377699">"ရပ်ပါ"</string>
     <string name="keyboard_key_media_next" msgid="8502476691227914952">"ရှေ့သို့"</string>
@@ -1405,8 +1419,7 @@
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"ပိုပြရန် သင်္ကေတ"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"သို့မဟုတ်"</string>
     <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"ဖိဆွဲအထိန်း"</string>
-    <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) -->
-    <skip />
+    <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"ကီးဘုတ်ဆက်တင်များ"</string>
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"သင့်ကီးဘုတ်ကိုသုံး၍ လမ်းညွှန်ခြင်း"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"လက်ကွက်ဖြတ်လမ်းများကို လေ့လာပါ"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"သင့်တာ့ချ်ပက်ကိုသုံး၍ လမ်းညွှန်ခြင်း"</string>
@@ -1464,8 +1477,6 @@
     <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"အက်ပ်များက ပံ့ပိုးထားသည်"</string>
     <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"ဖန်သားပြင်"</string>
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"အမျိုးအမည်မသိ"</string>
-    <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) -->
-    <skip />
-    <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) -->
-    <skip />
+    <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"အကွက်ငယ်များ ပြင်ဆင်သတ်မှတ်ခြင်း"</string>
+    <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"အကွက်ငယ်များကို ၎င်းတို့၏ မူလအစီအစဉ်နှင့် အရွယ်အစားများသို့ ပြင်ဆင်သတ်မှတ်မလား။"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml
index 03e9f35..1c30ca7 100644
--- a/packages/SystemUI/res/values-nb/strings.xml
+++ b/packages/SystemUI/res/values-nb/strings.xml
@@ -136,11 +136,17 @@
     <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Du tar nå opp <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Stopp opptaket"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Deler skjermen"</string>
+    <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
+    <skip />
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Vil du slutte å dele skjermen?"</string>
+    <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
+    <skip />
     <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Du deler nå hele skjermen med <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>"</string>
     <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Du deler nå hele skjermen med en app"</string>
     <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"Du deler nå <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>"</string>
     <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"Du deler nå en app"</string>
+    <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
+    <skip />
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Slutt å dele"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Caster skjermen"</string>
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Vil du stoppe castingen?"</string>
@@ -515,6 +521,10 @@
     <string name="communal_widget_picker_title" msgid="1953369090475731663">"Moduler på låseskjermen"</string>
     <string name="communal_widget_picker_description" msgid="490515450110487871">"Hvem som helst kan se moduler på låseskjermen – selv om nettbrettet er låst."</string>
     <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"velg bort modul"</string>
+    <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
+    <skip />
+    <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
+    <skip />
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Låseskjermmoduler"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"For å åpne en app ved hjelp av en modul må du bekrefte at det er deg. Husk også at hvem som helst kan se dem, selv om nettbrettet er låst. Noen moduler er kanskje ikke laget for å være på låseskjermen og kan være utrygge å legge til der."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Greit"</string>
@@ -571,6 +581,10 @@
     <string name="clear_all_notifications_text" msgid="348312370303046130">"Fjern alt"</string>
     <string name="manage_notifications_text" msgid="6885645344647733116">"Administrer"</string>
     <string name="manage_notifications_history_text" msgid="57055985396576230">"Logg"</string>
+    <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
+    <skip />
+    <!-- no translation found for notification_history_button_description (1578657591405033383) -->
+    <skip />
     <string name="notification_section_header_incoming" msgid="850925217908095197">"Ny"</string>
     <string name="notification_section_header_gentle" msgid="6804099527336337197">"Lydløs"</string>
     <string name="notification_section_header_alerting" msgid="5581175033680477651">"Varsler"</string>
@@ -1405,8 +1419,7 @@
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Vis-ikon"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"eller"</string>
     <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Håndtak"</string>
-    <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) -->
-    <skip />
+    <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Tastaturinnstillinger"</string>
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Naviger med tastaturet"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Lær deg hurtigtaster"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Naviger med styreflaten"</string>
@@ -1464,8 +1477,6 @@
     <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Levert av apper"</string>
     <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Skjerm"</string>
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Ukjent"</string>
-    <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) -->
-    <skip />
-    <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) -->
-    <skip />
+    <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"Tilbakestill brikkene"</string>
+    <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"Vil du tilbakestille brikkene til den opprinnelige rekkefølgen og størrelsen?"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml
index 4d165cc..f7ac880 100644
--- a/packages/SystemUI/res/values-ne/strings.xml
+++ b/packages/SystemUI/res/values-ne/strings.xml
@@ -136,11 +136,17 @@
     <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"तपाईं अहिले <xliff:g id="APP_NAME">%1$s</xliff:g> रेकर्ड गरिरहनुभएको छ"</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"रेकर्ड गर्न छाड्नुहोस्"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"स्क्रिन सेयर गरिँदै छ"</string>
+    <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
+    <skip />
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"स्क्रिन सेयर गर्न छाड्ने हो?"</string>
+    <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
+    <skip />
     <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"तपाईं अहिले <xliff:g id="HOST_APP_NAME">%1$s</xliff:g> सँग आफ्नो डिभाइसको पूरै स्क्रिन सेयर गरिरहनुभएको छ"</string>
     <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"तपाईं अहिले कुनै एपसँग आफ्नो डिभाइसको पूरै स्क्रिन सेयर गरिरहनुभएको छ"</string>
     <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"तपाईं अहिले <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> सेयर गरिरहनुभएको छ"</string>
     <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"तपाईं अहिले कुनै एप सेयर गरिरहनुभएको छ"</string>
+    <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
+    <skip />
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"सेयर गर्न छाड्नुहोस्"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"स्क्रिन कास्ट गरिँदै छ"</string>
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"कास्ट गर्न छाड्ने हो?"</string>
@@ -515,6 +521,10 @@
     <string name="communal_widget_picker_title" msgid="1953369090475731663">"लक स्क्रिन विजेटहरू"</string>
     <string name="communal_widget_picker_description" msgid="490515450110487871">"तपाईंको ट्याब्लेट लक भएका बेला पनि सबैले लक स्क्रिनमा भएका विजेट हेर्न सक्छन्।"</string>
     <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"विजेटको चयन रद्द गर्नुहोस्"</string>
+    <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
+    <skip />
+    <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
+    <skip />
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"लक स्क्रिन विजेटहरू"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"विजेट प्रयोग गरी एप खोल्न तपाईंले आफ्नो पहिचान पुष्टि गर्नु पर्ने हुन्छ। साथै, तपाईंको ट्याब्लेट लक भएका बेला पनि सबै जनाले तिनलाई देख्न सक्छन् भन्ने कुरा ख्याल गर्नुहोस्। केही विजेटहरू लक स्क्रिनमा प्रयोग गर्ने उद्देश्यले नबनाइएका हुन सक्छन् र तिनलाई यहाँ हाल्नु सुरक्षित नहुन सक्छ।"</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"बुझेँ"</string>
@@ -571,6 +581,10 @@
     <string name="clear_all_notifications_text" msgid="348312370303046130">"सबै हटाउनुहोस्"</string>
     <string name="manage_notifications_text" msgid="6885645344647733116">"व्यवस्थित गर्नुहोस्"</string>
     <string name="manage_notifications_history_text" msgid="57055985396576230">"हिस्ट्री"</string>
+    <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
+    <skip />
+    <!-- no translation found for notification_history_button_description (1578657591405033383) -->
+    <skip />
     <string name="notification_section_header_incoming" msgid="850925217908095197">"नयाँ"</string>
     <string name="notification_section_header_gentle" msgid="6804099527336337197">"साइलेन्ट"</string>
     <string name="notification_section_header_alerting" msgid="5581175033680477651">"सूचनाहरू"</string>
@@ -800,7 +814,7 @@
     <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string>
     <string name="keyboard_key_space" msgid="6980847564173394012">"स्पेस"</string>
     <string name="keyboard_key_enter" msgid="8633362970109751646">"Enter"</string>
-    <string name="keyboard_key_backspace" msgid="4095278312039628074">"ब्याकस्पेस"</string>
+    <string name="keyboard_key_backspace" msgid="4095278312039628074">"Backspace"</string>
     <string name="keyboard_key_media_play_pause" msgid="8389984232732277478">"प्ले गर्नुहोस्/पज गर्नुहोस्"</string>
     <string name="keyboard_key_media_stop" msgid="1509943745250377699">"रोक्नुहोस्"</string>
     <string name="keyboard_key_media_next" msgid="8502476691227914952">"अर्को"</string>
@@ -839,7 +853,7 @@
     <string name="keyboard_shortcut_a11y_filter_open_apps" msgid="6175417687221004059">"एपहरू खोल्ने सर्टकटहरू देखाइँदै छ"</string>
     <string name="keyboard_shortcut_a11y_filter_current_app" msgid="7944592357493737911">"हालको एपका लागि सर्टकटहरू देखाइँदै छ"</string>
     <string name="group_system_access_notification_shade" msgid="1619028907006553677">"सूचनाहरू हेर्नुहोस्"</string>
-    <string name="group_system_full_screenshot" msgid="5742204844232667785">"स्क्रिनसट खिच्नुहोस्"</string>
+    <string name="group_system_full_screenshot" msgid="5742204844232667785">"स्क्रिनसट लिनुहोस्"</string>
     <string name="group_system_access_system_app_shortcuts" msgid="8562482996626694026">"सर्टकटहरू देखाउनुहोस्"</string>
     <string name="group_system_go_back" msgid="2730322046244918816">"पछाडि जानुहोस्"</string>
     <string name="group_system_access_home_screen" msgid="4130366993484706483">"होम स्क्रिनमा जानुहोस्"</string>
@@ -1405,8 +1419,7 @@
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"\"एक्स्पान्ड गर्नुहोस्\" आइकन"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"वा"</string>
     <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"ड्र्याग ह्यान्डल"</string>
-    <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) -->
-    <skip />
+    <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"किबोर्डसम्बन्धी सेटिङ"</string>
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"किबोर्ड प्रयोग गरी नेभिगेट गर्नुहोस्"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"किबोर्डका सर्टकटहरू प्रयोग गर्न सिक्नुहोस्"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"टचप्याड प्रयोग गरी नेभिगेट गर्नुहोस्"</string>
@@ -1438,19 +1451,19 @@
     <string name="home_controls_dream_label" msgid="6567105701292324257">"होम कन्ट्रोलहरू"</string>
     <string name="home_controls_dream_description" msgid="4644150952104035789">"होम कन्ट्रोललाई तुरुन्तै स्क्रिनसेभरका रूपमा एक्सेस गर्नुहोस्"</string>
     <string name="volume_undo_action" msgid="5815519725211877114">"अन्डू गर्नुहोस्"</string>
-    <string name="back_edu_toast_content" msgid="4530314597378982956">"पछाडि जान तिन वटा औँलाले टचप्याडमा बायाँ वा दायाँतिर स्वाइप गर्नुहोस्"</string>
-    <string name="home_edu_toast_content" msgid="3381071147871955415">"होममा जान तिन वटा औँलाले टचप्याडमा माथितिर स्वाइप गर्नुहोस्"</string>
-    <string name="overview_edu_toast_content" msgid="5797030644017804518">"आफूले हालसालै चलाएका एपहरू हेर्न तिन वटा औँलाले टचप्याडमा माथितिर स्वाइप गर्नुहोस् र होल्ड गर्नुहोस्"</string>
+    <string name="back_edu_toast_content" msgid="4530314597378982956">"पछाडि जान तीन वटा औँलाले टचप्याडमा बायाँ वा दायाँतिर स्वाइप गर्नुहोस्"</string>
+    <string name="home_edu_toast_content" msgid="3381071147871955415">"होममा जान तीन वटा औँलाले टचप्याडमा माथितिर स्वाइप गर्नुहोस्"</string>
+    <string name="overview_edu_toast_content" msgid="5797030644017804518">"आफूले हालसालै चलाएका एपहरू हेर्न तीन वटा औँलाले टचप्याडमा माथितिर स्वाइप गर्नुहोस् र होल्ड गर्नुहोस्"</string>
     <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"आफ्ना सबै एपहरू हेर्न आफ्नो किबोर्डमा भएको एक्सन की थिच्नुहोस्"</string>
     <string name="redacted_notification_single_line_title" msgid="212019960919261670">"जानकारी लुकाउन सम्पादन गरिएको"</string>
     <string name="redacted_notification_single_line_text" msgid="8684166405005242945">"हेर्नका लागि अनलक गर्नुहोस्"</string>
     <string name="contextual_education_dialog_title" msgid="4630392552837487324">"सान्दर्भिक शिक्षा"</string>
     <string name="back_edu_notification_title" msgid="5624780717751357278">"पछाडि जान आफ्नो टचप्याड प्रयोग गर्नुहोस्"</string>
-    <string name="back_edu_notification_content" msgid="2497557451540954068">"तिन वटा औँला प्रयोग गरी बायाँ वा दायाँतिर स्वाइप गर्नुहोस्। थप जेस्चर प्रयोग गर्ने तरिका सिक्न ट्याप गर्नुहोस्।"</string>
+    <string name="back_edu_notification_content" msgid="2497557451540954068">"तीन वटा औँला प्रयोग गरी बायाँ वा दायाँतिर स्वाइप गर्नुहोस्। थप जेस्चर प्रयोग गर्ने तरिका सिक्न ट्याप गर्नुहोस्।"</string>
     <string name="home_edu_notification_title" msgid="6097902076909654045">"होममा जान आफ्नो टचप्याड प्रयोग गर्नुहोस्"</string>
-    <string name="home_edu_notification_content" msgid="6631697734535766588">"तिन वटा औँला प्रयोग गरी माथितिर स्वाइप गर्नुहोस्। थप जेस्चर प्रयोग गर्ने तरिका सिक्न ट्याप गर्नुहोस्।"</string>
+    <string name="home_edu_notification_content" msgid="6631697734535766588">"तीन वटा औँला प्रयोग गरी माथितिर स्वाइप गर्नुहोस्। थप जेस्चर प्रयोग गर्ने तरिका सिक्न ट्याप गर्नुहोस्।"</string>
     <string name="overview_edu_notification_title" msgid="1265824157319562406">"आफूले हालसालै चलाएका एपहरू हेर्न आफ्नो टचप्याड प्रयोग गर्नुहोस्"</string>
-    <string name="overview_edu_notification_content" msgid="3578204677648432500">"तिन वटा औँला प्रयोग गरी माथितिर स्वाइप गर्नुहोस् र होल्ड गर्नुहोस्। थप जेस्चर प्रयोग गर्ने तरिका सिक्न ट्याप गर्नुहोस्।"</string>
+    <string name="overview_edu_notification_content" msgid="3578204677648432500">"तीन वटा औँला प्रयोग गरी माथितिर स्वाइप गर्नुहोस् र होल्ड गर्नुहोस्। थप जेस्चर प्रयोग गर्ने तरिका सिक्न ट्याप गर्नुहोस्।"</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"सबै एपहरू हेर्न आफ्नो किबोर्ड प्रयोग गर्नुहोस्"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"जुनसुकै बेला एक्सन की थिच्नुहोस्। थप जेस्चर प्रयोग गर्ने तरिका सिक्न ट्याप गर्नुहोस्।"</string>
     <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="910988771011857460">"\"अझै मधुरो\" सुविधा अब चमक घटबढ गर्ने स्लाइडरमा समावेश गरिएको छ"</string>
@@ -1464,8 +1477,6 @@
     <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"एपले उपलब्ध गराएका"</string>
     <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"डिस्प्ले"</string>
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"अज्ञात"</string>
-    <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) -->
-    <skip />
-    <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) -->
-    <skip />
+    <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"टाइलहरू रिसेट गर्नुहोस्"</string>
+    <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"टाइलहरूको डिफल्ट क्रम र आकार रिसेट गर्ने हो?"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index 4249cb7..4cea605 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -136,11 +136,17 @@
     <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Je neemt op dit moment <xliff:g id="APP_NAME">%1$s</xliff:g> op"</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Opname stoppen"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Scherm delen"</string>
+    <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
+    <skip />
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Scherm delen stoppen?"</string>
+    <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
+    <skip />
     <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Je deelt op dit moment je hele scherm met <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>"</string>
     <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Je deelt op dit moment je hele scherm met een app"</string>
     <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"Je deelt op dit moment <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>"</string>
     <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"Je deelt op dit moment een app"</string>
+    <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
+    <skip />
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Delen stoppen"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Scherm casten"</string>
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Stoppen met casten?"</string>
@@ -515,6 +521,10 @@
     <string name="communal_widget_picker_title" msgid="1953369090475731663">"Widgets op het vergrendelscherm"</string>
     <string name="communal_widget_picker_description" msgid="490515450110487871">"Iedereen kan widgets op je vergrendelscherm bekijken, ook als je tablet vergrendeld is."</string>
     <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"widget deselecteren"</string>
+    <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
+    <skip />
+    <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
+    <skip />
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Widgets op het vergrendelscherm"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Als je een app wilt openen met een widget, moet je verifiëren dat jij het bent. Houd er ook rekening mee dat iedereen ze kan bekijken, ook als je tablet vergrendeld is. Bepaalde widgets zijn misschien niet bedoeld voor je vergrendelscherm en kunnen hier niet veilig worden toegevoegd."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"OK"</string>
@@ -571,6 +581,10 @@
     <string name="clear_all_notifications_text" msgid="348312370303046130">"Alles wissen"</string>
     <string name="manage_notifications_text" msgid="6885645344647733116">"Beheren"</string>
     <string name="manage_notifications_history_text" msgid="57055985396576230">"Geschiedenis"</string>
+    <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
+    <skip />
+    <!-- no translation found for notification_history_button_description (1578657591405033383) -->
+    <skip />
     <string name="notification_section_header_incoming" msgid="850925217908095197">"Nieuw"</string>
     <string name="notification_section_header_gentle" msgid="6804099527336337197">"Stil"</string>
     <string name="notification_section_header_alerting" msgid="5581175033680477651">"Meldingen"</string>
@@ -1405,8 +1419,7 @@
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Icoon voor uitvouwen"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"of"</string>
     <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Handgreep voor slepen"</string>
-    <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) -->
-    <skip />
+    <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Toetsenbordinstellingen"</string>
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Navigeren met je toetsenbord"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Leer sneltoetsen die je kunt gebruiken"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Navigeren met je touchpad"</string>
@@ -1464,8 +1477,6 @@
     <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Geleverd door apps"</string>
     <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Scherm"</string>
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Onbekend"</string>
-    <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) -->
-    <skip />
-    <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) -->
-    <skip />
+    <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"Tegels resetten"</string>
+    <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"Tegels resetten naar de oorspronkelijke volgorde en grootte?"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml
index e2cd9ec..bbbea61 100644
--- a/packages/SystemUI/res/values-or/strings.xml
+++ b/packages/SystemUI/res/values-or/strings.xml
@@ -136,11 +136,17 @@
     <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"ଆପଣ ବର୍ତ୍ତମାନ <xliff:g id="APP_NAME">%1$s</xliff:g>ର ବିଷୟବସ୍ତୁକୁ ରେକର୍ଡ କରୁଛନ୍ତି"</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"ରେକର୍ଡିଂ ବନ୍ଦ କରନ୍ତୁ"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"ସ୍କ୍ରିନ ସେୟାର କରାଯାଉଛି"</string>
+    <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
+    <skip />
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"ସ୍କ୍ରିନ ସେୟାର କରିବା ବନ୍ଦ କରିବେ?"</string>
+    <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
+    <skip />
     <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"ଆପଣ ବର୍ତ୍ତମାନ ଆପଣଙ୍କର ସମ୍ପୂର୍ଣ୍ଣ ସ୍କ୍ରିନକୁ <xliff:g id="HOST_APP_NAME">%1$s</xliff:g> ସହ ସେୟାର କରୁଛନ୍ତି"</string>
     <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"ଆପଣ ବର୍ତ୍ତମାନ ଆପଣଙ୍କର ସମ୍ପୂର୍ଣ୍ଣ ସ୍କ୍ରିନକୁ ଏକ ଆପ ସହ ସେୟାର କରୁଛନ୍ତି"</string>
     <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"ଆପଣ ବର୍ତ୍ତମାନ <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>କୁ ସେୟାର କରୁଛନ୍ତି"</string>
     <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"ଆପଣ ବର୍ତ୍ତମାନ ଏକ ଆପକୁ ସେୟାର କରୁଛନ୍ତି"</string>
+    <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
+    <skip />
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"ସେୟାର କରିବା ବନ୍ଦ କରନ୍ତୁ"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"ସ୍କ୍ରିନ କାଷ୍ଟ କରାଯାଉଛି"</string>
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"କାଷ୍ଟ କରିବା ବନ୍ଦ କରିବେ?"</string>
@@ -322,7 +328,7 @@
     <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"ଇନପୁଟ୍"</string>
     <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"ଶ୍ରବଣ ଯନ୍ତ୍ର"</string>
     <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"ଅନ୍ ହେଉଛି…"</string>
-    <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"ଅଟୋ-ରୋଟେଟ୍"</string>
+    <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"ଅଟୋ-ରୋଟେଟ"</string>
     <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"ଅଟୋ-ରୋଟେଟ ସ୍କ୍ରିନ"</string>
     <string name="quick_settings_location_label" msgid="2621868789013389163">"ଲୋକେସନ"</string>
     <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"ସ୍କ୍ରିନ ସେଭର"</string>
@@ -515,6 +521,10 @@
     <string name="communal_widget_picker_title" msgid="1953369090475731663">"ଲକ ସ୍କ୍ରିନ ୱିଜେଟ"</string>
     <string name="communal_widget_picker_description" msgid="490515450110487871">"ଆପଣଙ୍କ ଟାବଲେଟ ଲକ ଥିଲେ ମଧ୍ୟ ଯେ କୌଣସି ବ୍ୟକ୍ତି ଲକ ସ୍କ୍ରିନରେ ୱିଜେଟକୁ ଭ୍ୟୁ କରିପାରିବେ।"</string>
     <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"ୱିଜେଟକୁ ଅଚୟନ କରନ୍ତୁ"</string>
+    <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
+    <skip />
+    <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
+    <skip />
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"ଲକ ସ୍କ୍ରିନ ୱିଜେଟ"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"ଏକ ୱିଜେଟ ବ୍ୟବହାର କରି ଗୋଟିଏ ଆପ ଖୋଲିବା ପାଇଁ ଏହା ଆପଣ ଅଟନ୍ତି ବୋଲି ଆପଣଙ୍କୁ ଯାଞ୍ଚ କରିବାକୁ ହେବ। ଆହୁରି ମଧ୍ୟ, ଆପଣଙ୍କ ଟାବଲେଟ ଲକ ଥିଲେ ମଧ୍ୟ ଯେ କୌଣସି ବ୍ୟକ୍ତି ଏହାକୁ ଭ୍ୟୁ କରିପାରିବେ ବୋଲି ମନେ ରଖନ୍ତୁ। କିଛି ୱିଜେଟ ଆପଣଙ୍କ ଲକ ସ୍କ୍ରିନ ପାଇଁ ଉଦ୍ଦିଷ୍ଟ ହୋଇନଥାଇପାରେ ଏବଂ ଏଠାରେ ଯୋଗ କରିବା ଅସୁରକ୍ଷିତ ହୋଇପାରେ।"</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"ବୁଝିଗଲି"</string>
@@ -555,7 +565,7 @@
     <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"ସମ୍ପୂର୍ଣ୍ଣ ସ୍କ୍ରିନକୁ କାଷ୍ଟ କରନ୍ତୁ"</string>
     <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"ଆପଣ ଆପଣଙ୍କ ସମ୍ପୂର୍ଣ୍ଣ ସ୍କ୍ରିନକୁ କାଷ୍ଟ କରିବା ସମୟରେ ଆପଣଙ୍କ ସ୍କ୍ରିନରେ ସବୁକିଛି ଦେଖାଯାଏ। ତେଣୁ ପାସୱାର୍ଡ, ପେମେଣ୍ଟ ବିବରଣୀ, ମେସେଜ, ଫଟୋ ଏବଂ ଅଡିଓ ଓ ଭିଡିଓ ପରି ବିଷୟଗୁଡ଼ିକ ପ୍ରତି ସତର୍କ ରୁହନ୍ତୁ।"</string>
     <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"ଆପଣ ଏକ ଆପ କାଷ୍ଟ କରିବା ସମୟରେ, ସେହି ଆପରେ ଦେଖାଯାଉଥିବା କିମ୍ବା ପ୍ଲେ ହେଉଥିବା ସବୁକିଛି ଦେଖାଯାଏ। ତେଣୁ ପାସୱାର୍ଡ, ପେମେଣ୍ଟ ବିବରଣୀ, ମେସେଜ, ଫଟୋ ଏବଂ ଅଡିଓ ଓ ଭିଡିଓ ପରି ବିଷୟଗୁଡ଼ିକ ପ୍ରତି ସତର୍କ ରୁହନ୍ତୁ।"</string>
-    <string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"କାଷ୍ଟ ସ୍କ୍ରିନ"</string>
+    <string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"ସ୍କ୍ରିନ କାଷ୍ଟ କରନ୍ତୁ"</string>
     <string name="media_projection_entry_cast_app_selector_title" msgid="6323062146661922387">"କାଷ୍ଟ କରିବାକୁ ଆପ ବାଛନ୍ତୁ"</string>
     <string name="media_projection_entry_generic_permission_dialog_title" msgid="4519802931547483628">"ସେୟାରିଂ ଆରମ୍ଭ କରିବେ?"</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_entire_screen" msgid="5407906851409410209">"ଆପଣ ସେୟାର, ରେକର୍ଡ କିମ୍ବା କାଷ୍ଟ କରିବା ସମୟରେ, ଆପଣଙ୍କ ସ୍କ୍ରିନରେ ଦେଖାଯାଉଥିବା କିମ୍ବା ଆପଣଙ୍କ ଡିଭାଇସରେ ପ୍ଲେ ହେଉଥିବା ସବୁକିଛିକୁ Androidର ଆକ୍ସେସ ଅଛି। ତେଣୁ ପାସୱାର୍ଡ, ପେମେଣ୍ଟ ବିବରଣୀ, ମେସେଜ, ଫଟୋ ଏବଂ ଅଡିଓ ଓ ଭିଡିଓ ପରି ବିଷୟଗୁଡ଼ିକ ପ୍ରତି ସତର୍କ ରୁହନ୍ତୁ।"</string>
@@ -571,6 +581,10 @@
     <string name="clear_all_notifications_text" msgid="348312370303046130">"ସବୁ ଖାଲି କରନ୍ତୁ"</string>
     <string name="manage_notifications_text" msgid="6885645344647733116">"ପରିଚାଳନା କରନ୍ତୁ"</string>
     <string name="manage_notifications_history_text" msgid="57055985396576230">"ଇତିହାସ"</string>
+    <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
+    <skip />
+    <!-- no translation found for notification_history_button_description (1578657591405033383) -->
+    <skip />
     <string name="notification_section_header_incoming" msgid="850925217908095197">"ନୂଆ"</string>
     <string name="notification_section_header_gentle" msgid="6804099527336337197">"ନୀରବ"</string>
     <string name="notification_section_header_alerting" msgid="5581175033680477651">"ବିଜ୍ଞପ୍ତିଗୁଡ଼ିକ"</string>
@@ -1405,8 +1419,7 @@
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"ଆଇକନକୁ ବିସ୍ତାର କରନ୍ତୁ"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"କିମ୍ବା"</string>
     <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"ଡ୍ରାଗ ହେଣ୍ଡେଲ"</string>
-    <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) -->
-    <skip />
+    <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"କୀବୋର୍ଡ ସେଟିଂ"</string>
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"ଆପଣଙ୍କ କୀବୋର୍ଡ ବ୍ୟବହାର କରି ନାଭିଗେଟ କରନ୍ତୁ"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"କୀବୋର୍ଡ ସର୍ଟକଟଗୁଡ଼ିକ ବିଷୟରେ ଜାଣନ୍ତୁ"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"ଆପଣଙ୍କ ଟଚପେଡ ବ୍ୟବହାର କରି ନାଭିଗେଟ କରନ୍ତୁ"</string>
@@ -1464,8 +1477,6 @@
     <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"ଆପ୍ସ ଦ୍ୱାରା ପ୍ରଦାନ କରାଯାଇଛି"</string>
     <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"ଡିସପ୍ଲେ"</string>
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"ଅଜଣା"</string>
-    <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) -->
-    <skip />
-    <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) -->
-    <skip />
+    <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"ଟାଇଲଗୁଡ଼ିକୁ ରିସେଟ କରନ୍ତୁ"</string>
+    <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"ଟାଇଲଗୁଡ଼ିକୁ ସେଗୁଡ଼ିକର ମୂଳ କ୍ରମ ଏବଂ ସାଇଜ ଅନୁସାରେ ରିସେଟ କରିବେ?"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml
index 3c3102c..0170a71 100644
--- a/packages/SystemUI/res/values-pa/strings.xml
+++ b/packages/SystemUI/res/values-pa/strings.xml
@@ -136,11 +136,17 @@
     <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"ਤੁਸੀਂ ਫ਼ਿਲਹਾਲ <xliff:g id="APP_NAME">%1$s</xliff:g> ਨੂੰ ਰਿਕਾਰਡ ਕਰ ਰਹੇ ਹੋ"</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"ਰਿਕਾਰਡਿੰਗ ਬੰਦ ਕਰੋ"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"ਸਕ੍ਰੀਨ ਸਾਂਝੀ ਕੀਤੀ ਜਾ ਰਹੀ ਹੈ"</string>
+    <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
+    <skip />
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"ਕੀ ਸਕ੍ਰੀਨ ਨੂੰ ਸਾਂਝਾ ਕਰਨਾ ਬੰਦ ਕਰਨਾ ਹੈ?"</string>
+    <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
+    <skip />
     <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"ਤੁਸੀਂ ਫ਼ਿਲਹਾਲ <xliff:g id="HOST_APP_NAME">%1$s</xliff:g> ਨਾਲ ਆਪਣੀ ਪੂਰੀ ਸਕ੍ਰੀਨ ਨੂੰ ਸਾਂਝਾ ਕਰ ਰਹੇ ਹੋ"</string>
     <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"ਤੁਸੀਂ ਫ਼ਿਲਹਾਲ ਕਿਸੇ ਐਪ ਨਾਲ ਆਪਣੀ ਪੂਰੀ ਸਕ੍ਰੀਨ ਨੂੰ ਸਾਂਝਾ ਕਰ ਰਹੇ ਹੋ"</string>
     <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"ਤੁਸੀਂ ਫ਼ਿਲਹਾਲ <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> ਨੂੰ ਸਾਂਝਾ ਕਰ ਰਹੇ ਹੋ"</string>
     <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"ਤੁਸੀਂ ਫ਼ਿਲਹਾਲ ਕਿਸੇ ਐਪ ਨੂੰ ਸਾਂਝਾ ਕਰ ਰਹੇ ਹੋ"</string>
+    <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
+    <skip />
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"ਸਾਂਝਾਕਰਨ ਬੰਦ ਕਰੋ"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"ਸਕ੍ਰੀਨ \'ਤੇ ਕਾਸਟ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ"</string>
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"ਕੀ ਕਾਸਟ ਕਰਨਾ ਬੰਦ ਕਰਨਾ ਹੈ?"</string>
@@ -515,6 +521,10 @@
     <string name="communal_widget_picker_title" msgid="1953369090475731663">"ਲਾਕ ਸਕ੍ਰੀਨ ਵਿਜੇਟ"</string>
     <string name="communal_widget_picker_description" msgid="490515450110487871">"ਕੋਈ ਵੀ ਤੁਹਾਡੀ ਲਾਕ ਸਕ੍ਰੀਨ \'ਤੇ ਵਿਜੇਟ ਦੇਖ ਸਕਦਾ ਹੈ, ਭਾਵੇਂ ਤੁਹਾਡਾ ਟੈਬਲੈੱਟ ਲਾਕ ਹੋਵੇ।"</string>
     <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"ਵਿਜੇਟ ਨੂੰ ਅਣਚੁਣਿਆ ਕਰੋ"</string>
+    <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
+    <skip />
+    <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
+    <skip />
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"ਲਾਕ ਸਕ੍ਰੀਨ ਵਿਜੇਟ"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"ਵਿਜੇਟ ਦੀ ਵਰਤੋਂ ਕਰ ਕੇ ਐਪ ਖੋਲ੍ਹਣ ਲਈ, ਤੁਹਾਨੂੰ ਇਹ ਪੁਸ਼ਟੀ ਕਰਨ ਦੀ ਲੋੜ ਪਵੇਗੀ ਕਿ ਇਹ ਤੁਸੀਂ ਹੀ ਹੋ। ਨਾਲ ਹੀ, ਇਹ ਵੀ ਧਿਆਨ ਵਿੱਚ ਰੱਖੋ ਕਿ ਕੋਈ ਵੀ ਉਨ੍ਹਾਂ ਨੂੰ ਦੇਖ ਸਕਦਾ ਹੈ, ਭਾਵੇਂ ਤੁਹਾਡਾ ਟੈਬਲੈੱਟ ਲਾਕ ਹੋਵੇ। ਹੋ ਸਕਦਾ ਹੈ ਕਿ ਕੁਝ ਵਿਜੇਟ ਤੁਹਾਡੀ ਲਾਕ ਸਕ੍ਰੀਨ ਲਈ ਨਾ ਬਣੇ ਹੋਣ ਅਤੇ ਉਨ੍ਹਾਂ ਨੂੰ ਇੱਥੇ ਸ਼ਾਮਲ ਕਰਨਾ ਅਸੁਰੱਖਿਅਤ ਹੋਵੇ।"</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"ਸਮਝ ਲਿਆ"</string>
@@ -571,6 +581,10 @@
     <string name="clear_all_notifications_text" msgid="348312370303046130">"ਸਭ ਕਲੀਅਰ ਕਰੋ"</string>
     <string name="manage_notifications_text" msgid="6885645344647733116">"ਪ੍ਰਬੰਧਨ ਕਰੋ"</string>
     <string name="manage_notifications_history_text" msgid="57055985396576230">"ਇਤਿਹਾਸ"</string>
+    <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
+    <skip />
+    <!-- no translation found for notification_history_button_description (1578657591405033383) -->
+    <skip />
     <string name="notification_section_header_incoming" msgid="850925217908095197">"ਨਵੀਆਂ"</string>
     <string name="notification_section_header_gentle" msgid="6804099527336337197">"ਸ਼ਾਂਤ"</string>
     <string name="notification_section_header_alerting" msgid="5581175033680477651">"ਸੂਚਨਾਵਾਂ"</string>
@@ -1405,8 +1419,7 @@
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"ਪ੍ਰਤੀਕ ਦਾ ਵਿਸਤਾਰ ਕਰੋ"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ਜਾਂ"</string>
     <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"ਘਸੀਟਣ ਵਾਲਾ ਹੈਂਡਲ"</string>
-    <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) -->
-    <skip />
+    <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"ਕੀ-ਬੋਰਡ ਸੈਟਿੰਗਾਂ"</string>
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"ਆਪਣੇ ਕੀ-ਬੋਰਡ ਦੀ ਵਰਤੋਂ ਕਰ ਕੇ ਨੈਵੀਗੇਟ ਕਰੋ"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"ਕੀ-ਬੋਰਡ ਸ਼ਾਰਟਕੱਟ ਬਾਰੇ ਜਾਣੋ"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"ਆਪਣੇ ਟੱਚਪੈਡ ਦੀ ਵਰਤੋਂ ਕਰ ਕੇ ਨੈਵੀਗੇਟ ਕਰੋ"</string>
@@ -1464,8 +1477,6 @@
     <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"ਐਪਾਂ ਵੱਲੋਂ ਮੁਹੱਈਆ ਕੀਤਾ ਗਿਆ"</string>
     <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"ਡਿਸਪਲੇ"</string>
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"ਅਗਿਆਤ"</string>
-    <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) -->
-    <skip />
-    <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) -->
-    <skip />
+    <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"ਟਾਇਲਾਂ ਨੂੰ ਰੀਸੈੱਟ ਕਰੋ"</string>
+    <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"ਕੀ ਟਾਇਲਾਂ ਨੂੰ ਉਨ੍ਹਾਂ ਦੇ ਮੂਲ ਕ੍ਰਮ ਅਤੇ ਆਕਾਰਾਂ \'ਤੇ ਰੀਸੈੱਟ ਕਰਨਾ ਹੈ?"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml
index 5b0fa56..33a743f 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -136,11 +136,17 @@
     <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Obecnie nagrywasz widok aplikacji <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Zatrzymaj nagrywanie"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Udostępniam ekran"</string>
+    <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
+    <skip />
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Zatrzymać udostępnianie ekranu?"</string>
+    <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
+    <skip />
     <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Obecnie udostępniasz aplikacji <xliff:g id="HOST_APP_NAME">%1$s</xliff:g> cały widok ekranu"</string>
     <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Obecnie udostępniasz aplikacji cały widok ekranu"</string>
     <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"Obecnie udostępniasz aplikację <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>"</string>
     <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"Obecnie udostępniasz aplikację"</string>
+    <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
+    <skip />
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Zatrzymaj udostępnianie"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Przesyłam zawartość ekranu"</string>
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Zatrzymać przesyłanie?"</string>
@@ -515,6 +521,10 @@
     <string name="communal_widget_picker_title" msgid="1953369090475731663">"Widżety na ekranie blokady"</string>
     <string name="communal_widget_picker_description" msgid="490515450110487871">"Widżety są widoczne na ekranie blokady, nawet gdy tablet jest zablokowany."</string>
     <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"odznacz widżet"</string>
+    <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
+    <skip />
+    <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
+    <skip />
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Widżety na ekranie blokady"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Aby otworzyć aplikację za pomocą widżetu, musisz potwierdzić swoją tożsamość. Pamiętaj też, że każdy będzie mógł wyświetlić widżety nawet wtedy, gdy tablet będzie zablokowany. Niektóre widżety mogą nie być przeznaczone do umieszczenia na ekranie blokady i ich dodanie w tym miejscu może być niebezpieczne."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"OK"</string>
@@ -571,6 +581,10 @@
     <string name="clear_all_notifications_text" msgid="348312370303046130">"Usuń wszystkie"</string>
     <string name="manage_notifications_text" msgid="6885645344647733116">"Zarządzaj"</string>
     <string name="manage_notifications_history_text" msgid="57055985396576230">"Historia"</string>
+    <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
+    <skip />
+    <!-- no translation found for notification_history_button_description (1578657591405033383) -->
+    <skip />
     <string name="notification_section_header_incoming" msgid="850925217908095197">"Nowe"</string>
     <string name="notification_section_header_gentle" msgid="6804099527336337197">"Ciche"</string>
     <string name="notification_section_header_alerting" msgid="5581175033680477651">"Powiadomienia"</string>
@@ -1405,8 +1419,7 @@
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ikona rozwijania"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"lub"</string>
     <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Uchwyt do przeciągania"</string>
-    <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) -->
-    <skip />
+    <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Ustawienia klawiatury"</string>
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Nawiguj za pomocą klawiatury"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Dowiedz się więcej o skrótach klawiszowych"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Nawiguj za pomocą touchpada"</string>
@@ -1438,7 +1451,7 @@
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Sterowanie domem"</string>
     <string name="home_controls_dream_description" msgid="4644150952104035789">"Szybki dostęp do sterowania domem na wygaszaczu ekranu"</string>
     <string name="volume_undo_action" msgid="5815519725211877114">"Cofnij"</string>
-    <string name="back_edu_toast_content" msgid="4530314597378982956">"Aby przejść wstecz, przesuń w prawo lub lewo za pomocą 3 palców na touchpadzie."</string>
+    <string name="back_edu_toast_content" msgid="4530314597378982956">"Aby przejść wstecz, przesuń trzema palcami w prawo lub lewo na touchpadzie."</string>
     <string name="home_edu_toast_content" msgid="3381071147871955415">"Aby przejść do ekranu głównego, przesuń w górę za pomocą 3 palców na touchpadzie"</string>
     <string name="overview_edu_toast_content" msgid="5797030644017804518">"Aby wyświetlić ostatnie aplikacje, przesuń w górę za pomocą 3 palców na touchpadzie i przytrzymaj."</string>
     <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"Aby wyświetlić wszystkie swoje aplikacje, naciśnij klawisz działania na klawiaturze"</string>
@@ -1446,7 +1459,7 @@
     <string name="redacted_notification_single_line_text" msgid="8684166405005242945">"Odblokuj, aby zobaczyć"</string>
     <string name="contextual_education_dialog_title" msgid="4630392552837487324">"Edukacja kontekstowa"</string>
     <string name="back_edu_notification_title" msgid="5624780717751357278">"Przechodzenie wstecz za pomocą touchpada"</string>
-    <string name="back_edu_notification_content" msgid="2497557451540954068">"Przesuń w prawo lub lewo za pomocą 3 palców. Kliknij, aby poznać więcej gestów."</string>
+    <string name="back_edu_notification_content" msgid="2497557451540954068">"Przesuń trzema palcami w prawo lub lewo. Kliknij, aby poznać więcej gestów."</string>
     <string name="home_edu_notification_title" msgid="6097902076909654045">"Przechodzenie do ekranu głównego za pomocą touchpada"</string>
     <string name="home_edu_notification_content" msgid="6631697734535766588">"Przesuń w górę za pomocą 3 palców. Kliknij, aby poznać więcej gestów."</string>
     <string name="overview_edu_notification_title" msgid="1265824157319562406">"Wyświetlanie ostatnio używanych aplikacji za pomocą touchpada"</string>
@@ -1464,8 +1477,6 @@
     <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Z aplikacji"</string>
     <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Wyświetlacz"</string>
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Nieznane"</string>
-    <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) -->
-    <skip />
-    <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) -->
-    <skip />
+    <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"Zresetuj kafelki"</string>
+    <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"Zresetować kafelki do pierwotnej kolejności i rozmiarów?"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml
index 9386027..8041760 100644
--- a/packages/SystemUI/res/values-pt-rBR/strings.xml
+++ b/packages/SystemUI/res/values-pt-rBR/strings.xml
@@ -136,11 +136,17 @@
     <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Você está gravando o app <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Parar gravação"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Compartilhando a tela"</string>
+    <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
+    <skip />
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Parar o compartilhamento de tela?"</string>
+    <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
+    <skip />
     <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Você está compartilhando a tela inteira com o app <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>"</string>
     <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Você está compartilhando a tela inteira com um app"</string>
     <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"Você está compartilhando o app <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>"</string>
     <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"Você está compartilhando um app"</string>
+    <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
+    <skip />
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Interromper compartilhamento"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Transmitindo a tela"</string>
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Parar transmissão?"</string>
@@ -515,6 +521,10 @@
     <string name="communal_widget_picker_title" msgid="1953369090475731663">"Widgets da tela de bloqueio"</string>
     <string name="communal_widget_picker_description" msgid="490515450110487871">"Todos podem ver os widgets na tela de bloqueio, mesmo com o tablet bloqueado."</string>
     <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"desmarcar widget"</string>
+    <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
+    <skip />
+    <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
+    <skip />
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Widgets da tela de bloqueio"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Para abrir um app usando um widget, você precisa confirmar sua identidade. E não se esqueça que qualquer pessoa pode ver os widgets, mesmo com o tablet bloqueado. Além disso, alguns apps não foram criados para a tela de bloqueio, é melhor manter a segurança."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Entendi"</string>
@@ -571,6 +581,10 @@
     <string name="clear_all_notifications_text" msgid="348312370303046130">"Remover tudo"</string>
     <string name="manage_notifications_text" msgid="6885645344647733116">"Gerenciar"</string>
     <string name="manage_notifications_history_text" msgid="57055985396576230">"Histórico"</string>
+    <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
+    <skip />
+    <!-- no translation found for notification_history_button_description (1578657591405033383) -->
+    <skip />
     <string name="notification_section_header_incoming" msgid="850925217908095197">"Novas"</string>
     <string name="notification_section_header_gentle" msgid="6804099527336337197">"Silenciosas"</string>
     <string name="notification_section_header_alerting" msgid="5581175033680477651">"Notificações"</string>
@@ -1405,8 +1419,7 @@
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ícone \"Abrir\""</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ou"</string>
     <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Alça de arrastar"</string>
-    <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) -->
-    <skip />
+    <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Configurações do teclado"</string>
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Navegue usando o teclado"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Aprenda atalhos do teclado"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Navegue usando o touchpad"</string>
@@ -1464,8 +1477,6 @@
     <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Fornecidos por apps"</string>
     <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Exibição"</string>
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Desconhecidos"</string>
-    <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) -->
-    <skip />
-    <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) -->
-    <skip />
+    <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"Redefinir blocos"</string>
+    <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"Redefinir os blocos para a ordem e os tamanhos originais?"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml
index fcd9bba..f000998 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -136,11 +136,17 @@
     <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Neste momento, está a gravar a app <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Parar gravação"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"A partilhar o ecrã"</string>
+    <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
+    <skip />
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Parar a partilha do ecrã?"</string>
+    <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
+    <skip />
     <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Neste momento, está a partilhar todo o seu ecrã com a app <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>"</string>
     <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Neste momento, está a partilhar todo o seu ecrã com uma app"</string>
     <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"Neste momento, está a partilhar a app <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>"</string>
     <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"Neste momento, está a partilhar uma app"</string>
+    <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
+    <skip />
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Parar partilha"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"A transmitir o ecrã"</string>
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Parar a transmissão?"</string>
@@ -515,6 +521,10 @@
     <string name="communal_widget_picker_title" msgid="1953369090475731663">"Widgets do ecrã de bloqueio"</string>
     <string name="communal_widget_picker_description" msgid="490515450110487871">"Todos podem pode ver widgets no ecrã de bloqueio, mesmo com o tablet bloqueado."</string>
     <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"desmarcar widget"</string>
+    <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
+    <skip />
+    <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
+    <skip />
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Widgets do ecrã de bloqueio"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Para abrir uma app através de um widget, vai ter de validar a sua identidade. Além disso, tenha em atenção que qualquer pessoa pode ver os widgets, mesmo quando o tablet estiver bloqueado. Alguns widgets podem não se destinar ao ecrã de bloqueio e pode ser inseguro adicioná-los aqui."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"OK"</string>
@@ -571,6 +581,10 @@
     <string name="clear_all_notifications_text" msgid="348312370303046130">"Limpar tudo"</string>
     <string name="manage_notifications_text" msgid="6885645344647733116">"Gerir"</string>
     <string name="manage_notifications_history_text" msgid="57055985396576230">"Histórico"</string>
+    <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
+    <skip />
+    <!-- no translation found for notification_history_button_description (1578657591405033383) -->
+    <skip />
     <string name="notification_section_header_incoming" msgid="850925217908095197">"Nova"</string>
     <string name="notification_section_header_gentle" msgid="6804099527336337197">"Silencioso"</string>
     <string name="notification_section_header_alerting" msgid="5581175033680477651">"Notificações"</string>
@@ -1405,8 +1419,7 @@
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ícone de expandir"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ou"</string>
     <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Indicador para arrastar"</string>
-    <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) -->
-    <skip />
+    <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Definições do teclado"</string>
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Navegue com o teclado"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Aprenda atalhos de teclado"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Navegue com o touchpad"</string>
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index 9386027..8041760 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -136,11 +136,17 @@
     <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Você está gravando o app <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Parar gravação"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Compartilhando a tela"</string>
+    <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
+    <skip />
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Parar o compartilhamento de tela?"</string>
+    <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
+    <skip />
     <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Você está compartilhando a tela inteira com o app <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>"</string>
     <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Você está compartilhando a tela inteira com um app"</string>
     <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"Você está compartilhando o app <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>"</string>
     <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"Você está compartilhando um app"</string>
+    <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
+    <skip />
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Interromper compartilhamento"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Transmitindo a tela"</string>
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Parar transmissão?"</string>
@@ -515,6 +521,10 @@
     <string name="communal_widget_picker_title" msgid="1953369090475731663">"Widgets da tela de bloqueio"</string>
     <string name="communal_widget_picker_description" msgid="490515450110487871">"Todos podem ver os widgets na tela de bloqueio, mesmo com o tablet bloqueado."</string>
     <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"desmarcar widget"</string>
+    <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
+    <skip />
+    <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
+    <skip />
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Widgets da tela de bloqueio"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Para abrir um app usando um widget, você precisa confirmar sua identidade. E não se esqueça que qualquer pessoa pode ver os widgets, mesmo com o tablet bloqueado. Além disso, alguns apps não foram criados para a tela de bloqueio, é melhor manter a segurança."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Entendi"</string>
@@ -571,6 +581,10 @@
     <string name="clear_all_notifications_text" msgid="348312370303046130">"Remover tudo"</string>
     <string name="manage_notifications_text" msgid="6885645344647733116">"Gerenciar"</string>
     <string name="manage_notifications_history_text" msgid="57055985396576230">"Histórico"</string>
+    <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
+    <skip />
+    <!-- no translation found for notification_history_button_description (1578657591405033383) -->
+    <skip />
     <string name="notification_section_header_incoming" msgid="850925217908095197">"Novas"</string>
     <string name="notification_section_header_gentle" msgid="6804099527336337197">"Silenciosas"</string>
     <string name="notification_section_header_alerting" msgid="5581175033680477651">"Notificações"</string>
@@ -1405,8 +1419,7 @@
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ícone \"Abrir\""</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ou"</string>
     <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Alça de arrastar"</string>
-    <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) -->
-    <skip />
+    <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Configurações do teclado"</string>
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Navegue usando o teclado"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Aprenda atalhos do teclado"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Navegue usando o touchpad"</string>
@@ -1464,8 +1477,6 @@
     <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Fornecidos por apps"</string>
     <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Exibição"</string>
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Desconhecidos"</string>
-    <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) -->
-    <skip />
-    <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) -->
-    <skip />
+    <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"Redefinir blocos"</string>
+    <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"Redefinir os blocos para a ordem e os tamanhos originais?"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml
index 42d24cd..57a2a0c 100644
--- a/packages/SystemUI/res/values-ro/strings.xml
+++ b/packages/SystemUI/res/values-ro/strings.xml
@@ -136,11 +136,17 @@
     <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Înregistrezi <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Oprește înregistrarea"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Se permite accesul la ecran"</string>
+    <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
+    <skip />
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Oprești accesul la ecran?"</string>
+    <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
+    <skip />
     <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Permiți accesul <xliff:g id="HOST_APP_NAME">%1$s</xliff:g> la întregul ecran"</string>
     <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Permiți accesul unei aplicații la întregul ecran"</string>
     <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"Permiți accesul la <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>"</string>
     <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"Permiți accesul la o aplicație"</string>
+    <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
+    <skip />
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Nu mai permite accesul"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Se proiectează ecranul"</string>
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Oprești proiectarea?"</string>
@@ -515,6 +521,10 @@
     <string name="communal_widget_picker_title" msgid="1953369090475731663">"Widgeturi pe ecranul de blocare"</string>
     <string name="communal_widget_picker_description" msgid="490515450110487871">"Oricine poate vedea widgeturile pe ecranul de blocare, chiar cu tableta blocată"</string>
     <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"deselectează widgetul"</string>
+    <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
+    <skip />
+    <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
+    <skip />
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Widgeturi pe ecranul de blocare"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Pentru a deschide o aplicație folosind un widget, va trebui să-ți confirmi identitatea. În plus, reține că oricine poate să vadă widgeturile, chiar dacă tableta este blocată. Este posibil ca unele widgeturi să nu fi fost create pentru ecranul de blocare și poate fi nesigur să le adaugi aici."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"OK"</string>
@@ -571,6 +581,10 @@
     <string name="clear_all_notifications_text" msgid="348312370303046130">"Șterge toate notificările"</string>
     <string name="manage_notifications_text" msgid="6885645344647733116">"Gestionează"</string>
     <string name="manage_notifications_history_text" msgid="57055985396576230">"Istoric"</string>
+    <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
+    <skip />
+    <!-- no translation found for notification_history_button_description (1578657591405033383) -->
+    <skip />
     <string name="notification_section_header_incoming" msgid="850925217908095197">"Noi"</string>
     <string name="notification_section_header_gentle" msgid="6804099527336337197">"Silențioase"</string>
     <string name="notification_section_header_alerting" msgid="5581175033680477651">"Notificări"</string>
@@ -1405,8 +1419,7 @@
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Pictograma de extindere"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"sau"</string>
     <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Ghidaj de tragere"</string>
-    <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) -->
-    <skip />
+    <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Setările tastaturii"</string>
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Navighează folosind tastatura"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Învață comenzile rapide de la tastatură"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Navighează folosind touchpadul"</string>
@@ -1464,8 +1477,6 @@
     <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Oferite de aplicații"</string>
     <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Ecran"</string>
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Necunoscută"</string>
-    <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) -->
-    <skip />
-    <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) -->
-    <skip />
+    <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"Resetează cardurile"</string>
+    <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"Resetezi cardurile la ordinea și dimensiunile inițiale?"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index cbb89c0..507f709 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -136,11 +136,17 @@
     <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Вы записываете экран приложения \"<xliff:g id="APP_NAME">%1$s</xliff:g>\"."</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Остановить запись"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Демонстрация экрана"</string>
+    <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
+    <skip />
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Прекратить показ экрана?"</string>
+    <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
+    <skip />
     <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Вы демонстрируете свой экран в приложении \"<xliff:g id="HOST_APP_NAME">%1$s</xliff:g>\"."</string>
     <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Вы демонстрируете свой экран в приложении."</string>
     <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"Вы демонстрируете экран приложения \"<xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>\"."</string>
     <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"Вы демонстрируете экран приложения."</string>
+    <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
+    <skip />
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Прекратить показ"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Трансляция экрана"</string>
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Прекратить трансляцию?"</string>
@@ -515,6 +521,10 @@
     <string name="communal_widget_picker_title" msgid="1953369090475731663">"Виджеты на заблокированном экране"</string>
     <string name="communal_widget_picker_description" msgid="490515450110487871">"Они видны всем, даже если планшет заблокирован."</string>
     <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"отменить выбор виджета"</string>
+    <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
+    <skip />
+    <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
+    <skip />
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Виджеты на заблокированном экране"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Чтобы открыть приложение, используя виджет, вам нужно будет подтвердить свою личность. Обратите внимание, что виджеты видны всем, даже если планшет заблокирован. Некоторые виджеты не предназначены для использования на заблокированном экране. Добавлять их туда может быть небезопасно."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"ОК"</string>
@@ -571,6 +581,10 @@
     <string name="clear_all_notifications_text" msgid="348312370303046130">"Очистить все"</string>
     <string name="manage_notifications_text" msgid="6885645344647733116">"Настроить"</string>
     <string name="manage_notifications_history_text" msgid="57055985396576230">"История"</string>
+    <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
+    <skip />
+    <!-- no translation found for notification_history_button_description (1578657591405033383) -->
+    <skip />
     <string name="notification_section_header_incoming" msgid="850925217908095197">"Новое"</string>
     <string name="notification_section_header_gentle" msgid="6804099527336337197">"Без звука"</string>
     <string name="notification_section_header_alerting" msgid="5581175033680477651">"Уведомления"</string>
@@ -771,7 +785,7 @@
     <string name="notification_unblockable_call_desc" msgid="5907328164696532169">"Уведомления о звонках нельзя изменить."</string>
     <string name="notification_multichannel_desc" msgid="7414593090056236179">"Эту группу уведомлений нельзя настроить здесь."</string>
     <string name="notification_delegate_header" msgid="1264510071031479920">"Уведомление отправлено через прокси-сервер."</string>
-    <string name="notification_channel_dialog_title" msgid="6856514143093200019">"Показывать все уведомления приложения \"<xliff:g id="APP_NAME">%1$s</xliff:g>\""</string>
+    <string name="notification_channel_dialog_title" msgid="6856514143093200019">"<xliff:g id="APP_NAME">%1$s</xliff:g>: все уведомления"</string>
     <string name="see_more_title" msgid="7409317011708185729">"Ещё"</string>
     <string name="feedback_alerted" msgid="5192459808484271208">"Уровень важности этого уведомления был автоматически &lt;b&gt;повышен до \"По умолчанию\"&lt;/b&gt;."</string>
     <string name="feedback_silenced" msgid="9116540317466126457">"Уровень важности этого уведомления был автоматически &lt;/b&gt;понижен до \"Без звука\"&lt;/b&gt;."</string>
@@ -1405,8 +1419,7 @@
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Значок \"Развернуть\""</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"или"</string>
     <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Маркер перемещения"</string>
-    <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) -->
-    <skip />
+    <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Настройки клавиатуры"</string>
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Навигация с помощью клавиатуры"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Узнайте о сочетаниях клавиш."</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Навигация с помощью сенсорной панели"</string>
@@ -1446,7 +1459,7 @@
     <string name="redacted_notification_single_line_text" msgid="8684166405005242945">"Разблокируйте экран, чтобы посмотреть."</string>
     <string name="contextual_education_dialog_title" msgid="4630392552837487324">"Контекстные подсказки"</string>
     <string name="back_edu_notification_title" msgid="5624780717751357278">"Используйте сенсорную панель, чтобы возвращаться назад"</string>
-    <string name="back_edu_notification_content" msgid="2497557451540954068">"Для этого проведите тремя пальцами влево или вправо. Нажмите, чтобы посмотреть другие жесты."</string>
+    <string name="back_edu_notification_content" msgid="2497557451540954068">"Для этого проведите тремя пальцами влево или вправо. Чтобы посмотреть другие жесты, нажмите здесь."</string>
     <string name="home_edu_notification_title" msgid="6097902076909654045">"Используйте сенсорную панель, чтобы переходить на главный экран"</string>
     <string name="home_edu_notification_content" msgid="6631697734535766588">"Для этого проведите тремя пальцами вверх. Нажмите, чтобы посмотреть другие жесты."</string>
     <string name="overview_edu_notification_title" msgid="1265824157319562406">"Используйте сенсорную панель, чтобы смотреть список недавних приложений"</string>
@@ -1464,8 +1477,6 @@
     <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Приложения"</string>
     <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Экран"</string>
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Неизвестно"</string>
-    <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) -->
-    <skip />
-    <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) -->
-    <skip />
+    <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"Сброс параметров"</string>
+    <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"Сбросить порядок и размер параметров?"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml
index 1ff4316..fb9be30 100644
--- a/packages/SystemUI/res/values-si/strings.xml
+++ b/packages/SystemUI/res/values-si/strings.xml
@@ -136,11 +136,17 @@
     <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"ඔබ දැනට <xliff:g id="APP_NAME">%1$s</xliff:g> පටිගත කරමින් සිටී"</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"පටිගත කිරීම නවත්වන්න"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"තිරය ​​බෙදා ගැනීම"</string>
+    <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
+    <skip />
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"තිරය ​​බෙදා ගැනීම නවත්වන්න ද?"</string>
+    <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
+    <skip />
     <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"ඔබ දැනට ඔබේ සම්පූර්ණ තිරය <xliff:g id="HOST_APP_NAME">%1$s</xliff:g> සමග බෙදා ගනිමින් සිටී"</string>
     <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"ඔබ දැනට ඔබේ සම්පූර්ණ තිරය යෙදුමක් සමග බෙදා ගනිමින් සිටී"</string>
     <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"ඔබ දැනට <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> බෙදා ගනිමින් සිටී"</string>
     <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"ඔබ දැනට යෙදුමක් බෙදා ගනිමින් සිටී"</string>
+    <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
+    <skip />
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"බෙදා ගැනීම නවත්වන්න"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"විකාශ තිරය"</string>
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"විකාශය නවතන්න ද?"</string>
@@ -515,6 +521,10 @@
     <string name="communal_widget_picker_title" msgid="1953369090475731663">"අගුළු තිර විජට්"</string>
     <string name="communal_widget_picker_description" msgid="490515450110487871">"ඔබේ ටැබ්ලටය අගුළු දමා තිබුණත්, ඕනෑම කෙනෙකුට ඔබේ අගුළු තිරයෙහි විජට් බැලිය හැක."</string>
     <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"විජට් නොතෝරන්න"</string>
+    <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
+    <skip />
+    <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
+    <skip />
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"අගුළු තිර විජට්"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"විජට් එකක් භාවිතයෙන් යෙදුමක් විවෘත කිරීමට, ඔබට ඒ ඔබ බව සත්‍යාපනය කිරීමට අවශ්‍ය වනු ඇත. එසේම, ඔබේ ටැබ්ලටය අගුළු දමා ඇති විට පවා ඕනෑම කෙනෙකුට ඒවා බැලිය හැකි බව මතක තබා ගන්න. සමහර විජට් ඔබේ අගුළු තිරය සඳහා අදහස් කර නොතිබිය හැකි අතර මෙහි එක් කිරීමට අනාරක්ෂිත විය හැක."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"තේරුණා"</string>
@@ -571,6 +581,10 @@
     <string name="clear_all_notifications_text" msgid="348312370303046130">"සියල්ල හිස් කරන්න"</string>
     <string name="manage_notifications_text" msgid="6885645344647733116">"කළමනාකරණය කරන්න"</string>
     <string name="manage_notifications_history_text" msgid="57055985396576230">"ඉතිහාසය"</string>
+    <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
+    <skip />
+    <!-- no translation found for notification_history_button_description (1578657591405033383) -->
+    <skip />
     <string name="notification_section_header_incoming" msgid="850925217908095197">"නව"</string>
     <string name="notification_section_header_gentle" msgid="6804099527336337197">"නිහඬ"</string>
     <string name="notification_section_header_alerting" msgid="5581175033680477651">"දැනුම් දීම්"</string>
@@ -1405,8 +1419,7 @@
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"දිගහැරීම් නිරූපකය"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"හෝ"</string>
     <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"ඇදීම් හැඬලය"</string>
-    <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) -->
-    <skip />
+    <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"යතුරු පුවරු සැකසීම්"</string>
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"ඔබේ යතුරු පුවරුව භාවිතයෙන් සංචාලනය කරන්න"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"යතුරුපුවරු කෙටිමං ඉගෙන ගන්න"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"ඔබේ ස්පර්ශ පෑඩ් භාවිතයෙන් සංචාලනය කරන්න"</string>
@@ -1464,8 +1477,6 @@
     <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"යෙදුම් මගින් සපයනු ලැබේ"</string>
     <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"සංදර්ශකය"</string>
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"නොදනී"</string>
-    <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) -->
-    <skip />
-    <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) -->
-    <skip />
+    <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"ටයිල් යළි සකසන්න"</string>
+    <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"ටයිල් ඒවායේ මුල් අනුපිළිවෙලට සහ ප්‍රමාණයට යළි සකසන්න ද?"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml
index 5f5ead1..42a2613 100644
--- a/packages/SystemUI/res/values-sk/strings.xml
+++ b/packages/SystemUI/res/values-sk/strings.xml
@@ -136,11 +136,17 @@
     <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Momentálne nahrávate aplikáciu <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Zastaviť nahrávanie"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Zdieľa sa obrazovka"</string>
+    <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
+    <skip />
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Chcete prestať zdieľať obrazovku?"</string>
+    <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
+    <skip />
     <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Momentálne zdieľate celú obrazovku s aplikáciou <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>"</string>
     <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Momentálne zdieľate celú obrazovku s aplikáciou"</string>
     <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"Momentálne zdieľate aplikáciu <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>"</string>
     <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"Momentálne zdieľate aplikáciu"</string>
+    <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
+    <skip />
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Prestať zdieľať"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Prenáša sa obrazovka"</string>
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Chcete zastaviť prenos?"</string>
@@ -515,6 +521,10 @@
     <string name="communal_widget_picker_title" msgid="1953369090475731663">"Miniaplikácie na uzamknutej obrazovke"</string>
     <string name="communal_widget_picker_description" msgid="490515450110487871">"Miniaplikácie na uzamknutej obrazovke uvidia všetci, aj keď je tablet uzamknutý."</string>
     <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"zrušiť výber miniaplikácie"</string>
+    <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
+    <skip />
+    <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
+    <skip />
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Miniaplikácie na uzamknutej obrazovke"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Ak chcete otvoriť aplikáciu pomocou miniaplikácie, budete musieť overiť svoju totožnosť. Pamätajte, že si miniaplikáciu môže pozrieť ktokoľvek, aj keď máte tablet uzamknutý. Niektoré miniaplikácie možno nie sú určené pre uzamknutú obrazovku a ich pridanie tu môže byť nebezpečné."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Dobre"</string>
@@ -571,6 +581,10 @@
     <string name="clear_all_notifications_text" msgid="348312370303046130">"Vymazať všetko"</string>
     <string name="manage_notifications_text" msgid="6885645344647733116">"Spravovať"</string>
     <string name="manage_notifications_history_text" msgid="57055985396576230">"História"</string>
+    <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
+    <skip />
+    <!-- no translation found for notification_history_button_description (1578657591405033383) -->
+    <skip />
     <string name="notification_section_header_incoming" msgid="850925217908095197">"Nové"</string>
     <string name="notification_section_header_gentle" msgid="6804099527336337197">"Tichý"</string>
     <string name="notification_section_header_alerting" msgid="5581175033680477651">"Upozornenia"</string>
@@ -1405,8 +1419,7 @@
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ikona rozbalenia"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"alebo"</string>
     <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Presúvadlo"</string>
-    <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) -->
-    <skip />
+    <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Nastavenia klávesnice"</string>
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Prechádzajte pomocou klávesnice"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Naučte sa klávesové skratky"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Prechádzajte pomocou touchpadu"</string>
@@ -1464,8 +1477,6 @@
     <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Poskytnuté aplikáciami"</string>
     <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Zobrazovanie"</string>
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Neznáme"</string>
-    <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) -->
-    <skip />
-    <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) -->
-    <skip />
+    <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"Resetovanie kariet"</string>
+    <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"Chcete resetovať karty na pôvodné poradie a veľkosti?"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml
index 3c027d9..4ca7b24 100644
--- a/packages/SystemUI/res/values-sl/strings.xml
+++ b/packages/SystemUI/res/values-sl/strings.xml
@@ -136,11 +136,17 @@
     <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Trenutno snemate aplikacijo <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Ustavi snemanje"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Deljenje zaslona"</string>
+    <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
+    <skip />
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Želite ustaviti deljenje zaslona?"</string>
+    <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
+    <skip />
     <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Trenutno delite celotni zaslon z aplikacijo <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>"</string>
     <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Trenutno delite celotni zaslon z eno od aplikacij"</string>
     <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"Trenutno delite aplikacijo <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>"</string>
     <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"Trenutno delite eno od aplikacij"</string>
+    <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
+    <skip />
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Ustavi deljenje"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Predvajanje vsebine zaslona"</string>
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Želite ustaviti predvajanje?"</string>
@@ -515,6 +521,10 @@
     <string name="communal_widget_picker_title" msgid="1953369090475731663">"Pripomočki na zaklenjenem zaslonu"</string>
     <string name="communal_widget_picker_description" msgid="490515450110487871">"Pripomočki na zaklenjenem zaslonu so vidni vsem, tudi če je tablica zaklenjena."</string>
     <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"preklic izbire pripomočka"</string>
+    <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
+    <skip />
+    <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
+    <skip />
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Pripomočki na zaklenjenem zaslonu"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Če želite aplikacijo odpreti s pripomočkom, morate potrditi, da ste to vi. Upoštevajte tudi, da si jih lahko ogledajo vsi, tudi ko je tablični računalnik zaklenjen. Nekateri pripomočki morda niso predvideni za uporabo na zaklenjenem zaslonu, zato jih tukaj morda ni varno dodati."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Razumem"</string>
@@ -571,6 +581,10 @@
     <string name="clear_all_notifications_text" msgid="348312370303046130">"Izbriši vse"</string>
     <string name="manage_notifications_text" msgid="6885645344647733116">"Upravljaj"</string>
     <string name="manage_notifications_history_text" msgid="57055985396576230">"Zgodovina"</string>
+    <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
+    <skip />
+    <!-- no translation found for notification_history_button_description (1578657591405033383) -->
+    <skip />
     <string name="notification_section_header_incoming" msgid="850925217908095197">"Novo"</string>
     <string name="notification_section_header_gentle" msgid="6804099527336337197">"Tiho"</string>
     <string name="notification_section_header_alerting" msgid="5581175033680477651">"Obvestila"</string>
@@ -800,7 +814,7 @@
     <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string>
     <string name="keyboard_key_space" msgid="6980847564173394012">"Preslednica"</string>
     <string name="keyboard_key_enter" msgid="8633362970109751646">"Vnesi"</string>
-    <string name="keyboard_key_backspace" msgid="4095278312039628074">"Premik nazaj"</string>
+    <string name="keyboard_key_backspace" msgid="4095278312039628074">"Vračalka"</string>
     <string name="keyboard_key_media_play_pause" msgid="8389984232732277478">"Predvajaj/zaustavi"</string>
     <string name="keyboard_key_media_stop" msgid="1509943745250377699">"Ustavi"</string>
     <string name="keyboard_key_media_next" msgid="8502476691227914952">"Naslednji"</string>
@@ -1405,12 +1419,11 @@
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ikona za razširitev"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ali"</string>
     <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Ročica za vlečenje"</string>
-    <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) -->
-    <skip />
+    <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Nastavitve tipkovnice"</string>
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Krmarjenje s tipkovnico"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Učenje bližnjičnih tipk"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Krmarjenje s sledilno ploščico"</string>
-    <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Učenje potez na sledilni ploščici"</string>
+    <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Naučite se uporabljati poteze na sledilni ploščici."</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Krmarjenje s tipkovnico in sledilno ploščico"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Učenje potez na sledilni ploščici, bližnjičnih tipk in drugega"</string>
     <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Nazaj"</string>
@@ -1464,8 +1477,6 @@
     <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Zagotavljajo aplikacije"</string>
     <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Zaslon"</string>
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Neznano"</string>
-    <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) -->
-    <skip />
-    <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) -->
-    <skip />
+    <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"Ponastavitev ploščic"</string>
+    <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"Želite ponastaviti ploščice na prvotni vrstni red in prvotno velikost?"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml
index e6f84f4..ddea921 100644
--- a/packages/SystemUI/res/values-sq/strings.xml
+++ b/packages/SystemUI/res/values-sq/strings.xml
@@ -136,11 +136,17 @@
     <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Po regjistron aktualisht \"<xliff:g id="APP_NAME">%1$s</xliff:g>\""</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Ndalo regjistrimin"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Ekrani po ndahet"</string>
+    <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
+    <skip />
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Të ndalohet ndarja e ekranit?"</string>
+    <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
+    <skip />
     <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Po ndan aktualisht të gjithë ekranin me \"<xliff:g id="HOST_APP_NAME">%1$s</xliff:g>\""</string>
     <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Po ndan aktualisht të gjithë ekranin me një aplikacion"</string>
     <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"Po ndan aktualisht \"<xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>\""</string>
     <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"Po ndan aktualisht një aplikacion"</string>
+    <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
+    <skip />
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Ndalo ndarjen"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Po transmeton ekranin"</string>
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Të ndalohet transmetimi?"</string>
@@ -515,6 +521,10 @@
     <string name="communal_widget_picker_title" msgid="1953369090475731663">"Miniaplikacionet në ekranin e kyçjes"</string>
     <string name="communal_widget_picker_description" msgid="490515450110487871">"Çdo person mund të shikojë miniaplikacionet në ekranin tënd të kyçjes, edhe nëse tableti është i kyçur."</string>
     <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"anulo zgjedhjen e miniaplikacionit"</string>
+    <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
+    <skip />
+    <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
+    <skip />
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Miniaplikacionet në ekranin e kyçjes"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Për të hapur një aplikacion duke përdorur një miniaplikacion, do të duhet të verifikosh që je ti. Ki parasysh gjithashtu që çdo person mund t\'i shikojë, edhe kur tableti yt është i kyçur. Disa miniaplikacione mund të mos jenë planifikuar për ekranin tënd të kyçjes dhe mund të mos jetë e sigurt t\'i shtosh këtu."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"E kuptova"</string>
@@ -571,6 +581,10 @@
     <string name="clear_all_notifications_text" msgid="348312370303046130">"Pastroji të gjitha"</string>
     <string name="manage_notifications_text" msgid="6885645344647733116">"Menaxho"</string>
     <string name="manage_notifications_history_text" msgid="57055985396576230">"Historiku"</string>
+    <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
+    <skip />
+    <!-- no translation found for notification_history_button_description (1578657591405033383) -->
+    <skip />
     <string name="notification_section_header_incoming" msgid="850925217908095197">"Të reja"</string>
     <string name="notification_section_header_gentle" msgid="6804099527336337197">"Në heshtje"</string>
     <string name="notification_section_header_alerting" msgid="5581175033680477651">"Njoftimet"</string>
@@ -1405,8 +1419,7 @@
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ikona e zgjerimit"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ose"</string>
     <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Doreza e zvarritjes"</string>
-    <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) -->
-    <skip />
+    <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Cilësimet e tastierës"</string>
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Navigo duke përdorur tastierën tënde"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Mëso shkurtoret e tastierës"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Navigo duke përdorur bllokun me prekje"</string>
@@ -1464,8 +1477,6 @@
     <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Mundësuar nga aplikacionet"</string>
     <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Ekrani"</string>
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Nuk njihet"</string>
-    <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) -->
-    <skip />
-    <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) -->
-    <skip />
+    <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"Rivendos pllakëzat"</string>
+    <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"Të rivendosen pllakëzat në rendin dhe madhësinë e tyre origjinale?"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml
index e82e2a2..5936cd7 100644
--- a/packages/SystemUI/res/values-sr/strings.xml
+++ b/packages/SystemUI/res/values-sr/strings.xml
@@ -72,7 +72,7 @@
     <string name="usb_disable_contaminant_detection" msgid="3827082183595978641">"Омогући USB"</string>
     <string name="learn_more" msgid="4690632085667273811">"Сазнајте више"</string>
     <string name="global_action_screenshot" msgid="2760267567509131654">"Снимак екрана"</string>
-    <string name="global_action_smart_lock_disabled" msgid="6286551337177954859">"Продужено откључавање је онемогућено"</string>
+    <string name="global_action_smart_lock_disabled" msgid="6286551337177954859">"Продужено откључано је онемогућено"</string>
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"је послао/ла слику"</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"Чување снимка екрана..."</string>
     <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"Снимак екрана се чува на пословном профилу…"</string>
@@ -136,11 +136,17 @@
     <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Тренутно снимате: <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Заустави снимање"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Екран се дели"</string>
+    <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
+    <skip />
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Желите да зауставите дељење екрана?"</string>
+    <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
+    <skip />
     <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Тренутно делите цео екран са: <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>"</string>
     <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Тренутно делите цео екран са апликацијом"</string>
     <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"Тренутно делите: <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>"</string>
     <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"Тренутно делите апликацију"</string>
+    <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
+    <skip />
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Заустави дељење"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Пребацује се екран"</string>
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Желите да зауставите пребацивање?"</string>
@@ -515,6 +521,10 @@
     <string name="communal_widget_picker_title" msgid="1953369090475731663">"Виџети за закључани екран"</string>
     <string name="communal_widget_picker_description" msgid="490515450110487871">"Сви могу да виде виџете на закључаном екрану, чак и када је таблет закључан."</string>
     <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"поништи избор виџета"</string>
+    <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
+    <skip />
+    <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
+    <skip />
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Виџети за закључани екран"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Да бисте отворили апликацију која користи виџет, треба да потврдите да сте то ви. Имајте у виду да свако може да га види, чак и када је таблет закључан. Неки виџети можда нису намењени за закључани екран и можда није безбедно да их тамо додате."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Важи"</string>
@@ -555,7 +565,7 @@
     <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"Пребаци цео екран"</string>
     <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"Када пребацујете цео екран, види се све што је на њему. Зато пазите на лозинке, информације о плаћању, поруке, слике, аудио и видео садржај."</string>
     <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"Када пребацујете апликацију, види се сав садржај који се приказује или пушта у њој. Зато пазите на лозинке, информације о плаћању, поруке, слике, аудио и видео садржај."</string>
-    <string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"Пребации екран"</string>
+    <string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"Пребаци екран"</string>
     <string name="media_projection_entry_cast_app_selector_title" msgid="6323062146661922387">"Одаберите апликацију коју желите да пребаците"</string>
     <string name="media_projection_entry_generic_permission_dialog_title" msgid="4519802931547483628">"Желите да почнете да делите?"</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_entire_screen" msgid="5407906851409410209">"Када делите, снимате или пребацујете, Android има приступ комплетном садржају који је видљив на екрану или се пушта на уређају. Зато пазите на лозинке, информације о плаћању, поруке, слике, и аудио и видео садржај."</string>
@@ -571,6 +581,10 @@
     <string name="clear_all_notifications_text" msgid="348312370303046130">"Обриши све"</string>
     <string name="manage_notifications_text" msgid="6885645344647733116">"Управљај"</string>
     <string name="manage_notifications_history_text" msgid="57055985396576230">"Историја"</string>
+    <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
+    <skip />
+    <!-- no translation found for notification_history_button_description (1578657591405033383) -->
+    <skip />
     <string name="notification_section_header_incoming" msgid="850925217908095197">"Ново"</string>
     <string name="notification_section_header_gentle" msgid="6804099527336337197">"Нечујно"</string>
     <string name="notification_section_header_alerting" msgid="5581175033680477651">"Обавештења"</string>
@@ -849,13 +863,13 @@
     <string name="group_system_access_all_apps_search" msgid="1553588630154197469">"Отвори листу апликација"</string>
     <string name="group_system_access_system_settings" msgid="8731721963449070017">"Отвори подешавања"</string>
     <string name="group_system_access_google_assistant" msgid="7210074957915968110">"Отвори Помоћник"</string>
-    <string name="group_system_lock_screen" msgid="7391191300363416543">"Закључавање екрана"</string>
+    <string name="group_system_lock_screen" msgid="7391191300363416543">"Откључавање екрана"</string>
     <string name="group_system_quick_memo" msgid="3764560265935722903">"Направи белешку"</string>
     <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Обављање више задатака истовремено"</string>
-    <string name="system_multitasking_rhs" msgid="8714224917276297810">"Користите подељени екран са актуелном апликацијом с десне стране"</string>
-    <string name="system_multitasking_lhs" msgid="8402954791206308783">"Користите подељени екран са актуелном апликацијом с леве стране"</string>
+    <string name="system_multitasking_rhs" msgid="8714224917276297810">"Користи подељени екран са том апликацијом с десне стране"</string>
+    <string name="system_multitasking_lhs" msgid="8402954791206308783">"Користи подељени екран са том апликацијом с леве стране"</string>
     <string name="system_multitasking_full_screen" msgid="336048080383640562">"Пређи са подељеног екрана на цео екран"</string>
-    <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Пређите у апликацију здесна или испод док користите подељени екран"</string>
+    <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Пређи у апликацију здесна или испод док је подељен екран"</string>
     <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Пређите у апликацију слева или изнад док користите подељени екран"</string>
     <string name="system_multitasking_replace" msgid="7410071959803642125">"У режиму подељеног екрана: замена једне апликације другом"</string>
     <string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Унос"</string>
@@ -1405,8 +1419,7 @@
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Икона за проширивање"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"или"</string>
     <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Маркер за превлачење"</string>
-    <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) -->
-    <skip />
+    <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Подешавања тастатуре"</string>
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Крећите се помоћу тастатуре"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Сазнајте више о тастерским пречицама"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Крећите се помоћу тачпеда"</string>
@@ -1438,14 +1451,14 @@
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Контроле за дом"</string>
     <string name="home_controls_dream_description" msgid="4644150952104035789">"Брз приступ контролама за дом као чувару екрана"</string>
     <string name="volume_undo_action" msgid="5815519725211877114">"Опозови"</string>
-    <string name="back_edu_toast_content" msgid="4530314597378982956">"Да бисте се вратили, превуците улево или удесно са три прста на тачпеду"</string>
+    <string name="back_edu_toast_content" msgid="4530314597378982956">"За назад, превуците улево или удесно са три прста на тачпеду"</string>
     <string name="home_edu_toast_content" msgid="3381071147871955415">"Да бисте отишли на почетни екран, превуците нагоре са три прста на тачпеду"</string>
     <string name="overview_edu_toast_content" msgid="5797030644017804518">"Да бисте прегледали недавне апликације, превуците нагоре и задржите са три прста на тачпеду"</string>
     <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"Да бисте погледали све апликације, притисните тастер радњи на тастатури"</string>
     <string name="redacted_notification_single_line_title" msgid="212019960919261670">"Редиговано"</string>
     <string name="redacted_notification_single_line_text" msgid="8684166405005242945">"Откључајте за приказ"</string>
     <string name="contextual_education_dialog_title" msgid="4630392552837487324">"Контекстуално образовање"</string>
-    <string name="back_edu_notification_title" msgid="5624780717751357278">"Користите тачпед да бисте се вратили"</string>
+    <string name="back_edu_notification_title" msgid="5624780717751357278">"Користите тачпед за враћање назад"</string>
     <string name="back_edu_notification_content" msgid="2497557451540954068">"Превуците улево или удесно са три прста. Додирните да бисте видели више покрета."</string>
     <string name="home_edu_notification_title" msgid="6097902076909654045">"Користите тачпед да бисте отишли на почетни екран"</string>
     <string name="home_edu_notification_content" msgid="6631697734535766588">"Превуците нагоре са три прста. Додирните да бисте видели више покрета."</string>
@@ -1464,8 +1477,6 @@
     <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Обезбеђују апликације"</string>
     <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Екран"</string>
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Непознато"</string>
-    <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) -->
-    <skip />
-    <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) -->
-    <skip />
+    <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"Ресетујте плочице"</string>
+    <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"Желите да ресетујете плочице на првобитни редослед и величине?"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index 06e4607..45e150a 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -136,11 +136,17 @@
     <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Du spelar för närvarande in <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Sluta spela in"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Skärmen delas"</string>
+    <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
+    <skip />
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Vill du sluta dela skärmen?"</string>
+    <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
+    <skip />
     <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Du delar för närvarande hela din skärm med <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>"</string>
     <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Du delar för närvarande hela din skärm med en app"</string>
     <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"Du delar för närvarande <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>"</string>
     <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"Du delar för närvarande en app"</string>
+    <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
+    <skip />
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Sluta dela"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Skärmen castas"</string>
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Vill du sluta att casta?"</string>
@@ -515,6 +521,10 @@
     <string name="communal_widget_picker_title" msgid="1953369090475731663">"Widgetar för låsskärm"</string>
     <string name="communal_widget_picker_description" msgid="490515450110487871">"Vem som helst kan se widgetar på din låsskärm, även om surfplattan är låst."</string>
     <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"avmarkera widget"</string>
+    <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
+    <skip />
+    <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
+    <skip />
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Widgetar för låsskärm"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Du måste verifiera din identitet innan du öppnar en app med en widget. Tänk också på att alla kan se dem, även när surfplattan är låst. Vissa widgetar kanske inte är avsedda för låsskärmen och det kan vara osäkert att lägga till dem här."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"OK"</string>
@@ -571,6 +581,10 @@
     <string name="clear_all_notifications_text" msgid="348312370303046130">"Rensa alla"</string>
     <string name="manage_notifications_text" msgid="6885645344647733116">"Hantera"</string>
     <string name="manage_notifications_history_text" msgid="57055985396576230">"Historik"</string>
+    <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
+    <skip />
+    <!-- no translation found for notification_history_button_description (1578657591405033383) -->
+    <skip />
     <string name="notification_section_header_incoming" msgid="850925217908095197">"Ny"</string>
     <string name="notification_section_header_gentle" msgid="6804099527336337197">"Ljudlöst"</string>
     <string name="notification_section_header_alerting" msgid="5581175033680477651">"Aviseringar"</string>
@@ -855,7 +869,7 @@
     <string name="system_multitasking_rhs" msgid="8714224917276297810">"Anänd delad skärm med den aktuella appen till höger"</string>
     <string name="system_multitasking_lhs" msgid="8402954791206308783">"Använd delad skärm med den aktuella appen till vänster"</string>
     <string name="system_multitasking_full_screen" msgid="336048080383640562">"Byt mellan delad skärm och helskärm"</string>
-    <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Byt till appen till vänster eller nedanför när du använder delad skärm"</string>
+    <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Byt till appen till höger eller nedanför när du använder delad skärm"</string>
     <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Byt till appen till vänster eller ovanför när du använder delad skärm"</string>
     <string name="system_multitasking_replace" msgid="7410071959803642125">"Med delad skärm: ersätt en app med en annan"</string>
     <string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Inmatning"</string>
@@ -1405,8 +1419,7 @@
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ikonen Utöka"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"eller"</string>
     <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Handtag"</string>
-    <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) -->
-    <skip />
+    <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Tangentbordsinställningar"</string>
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Navigera med tangentbordet"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Lär dig kortkommandon"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Navigera med styrplattan"</string>
@@ -1464,8 +1477,6 @@
     <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Tillhandahålls av appar"</string>
     <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Skärm"</string>
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Okänt"</string>
-    <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) -->
-    <skip />
-    <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) -->
-    <skip />
+    <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"Återställ rutor"</string>
+    <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"Vill du återställa rutorna till den ursprungliga ordningen och storleken?"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml
index b8a9dc9..86ea7e4 100644
--- a/packages/SystemUI/res/values-sw/strings.xml
+++ b/packages/SystemUI/res/values-sw/strings.xml
@@ -136,11 +136,17 @@
     <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Kwa sasa unarekodi <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Acha kurekodi"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Inaruhusu ufikiaji kwenye skrini"</string>
+    <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
+    <skip />
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Ungependa kuacha kuonyesha skrini?"</string>
+    <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
+    <skip />
     <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Kwa sasa unatuma maudhui yaliyo katika skrini yako nzima kwenye <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>"</string>
     <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Kwa sasa unatuma maudhui yaliyo katika skrini yako nzima kwenye programu"</string>
     <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"Kwa sasa unaonyesha <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>"</string>
     <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"Kwa sasa unatumia programu pamoja na wengine"</string>
+    <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
+    <skip />
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Acha kuonyesha skrini"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Inatuma skrini"</string>
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Ungependa kuacha kutuma?"</string>
@@ -515,6 +521,10 @@
     <string name="communal_widget_picker_title" msgid="1953369090475731663">"Wijeti zinazoonekana kwenye skrini iliyofungwa"</string>
     <string name="communal_widget_picker_description" msgid="490515450110487871">"Yeyote anaweza kuona wijeti kwenye skrini yako iliyofungwa, hata ikiwa umefunga kishikwambi chako."</string>
     <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"acha kuchagua wijeti"</string>
+    <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
+    <skip />
+    <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
+    <skip />
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Wijeti zinazoonekana kwenye skrini iliyofungwa"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Utahitaji kuthibitisha kuwa ni wewe ili ufungue programu ukitumia wijeti. Pia, kumbuka kuwa mtu yeyote anaweza kuziona, hata kishikwambi chako kikiwa kimefungwa. Huenda baadhi ya wijeti hazikukusudiwa kutumika kwenye skrini yako iliyofungwa na huenda si salama kuziweka hapa."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Nimeelewa"</string>
@@ -571,6 +581,10 @@
     <string name="clear_all_notifications_text" msgid="348312370303046130">"Futa zote"</string>
     <string name="manage_notifications_text" msgid="6885645344647733116">"Dhibiti"</string>
     <string name="manage_notifications_history_text" msgid="57055985396576230">"Historia"</string>
+    <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
+    <skip />
+    <!-- no translation found for notification_history_button_description (1578657591405033383) -->
+    <skip />
     <string name="notification_section_header_incoming" msgid="850925217908095197">"Mpya"</string>
     <string name="notification_section_header_gentle" msgid="6804099527336337197">"Kimya"</string>
     <string name="notification_section_header_alerting" msgid="5581175033680477651">"Arifa"</string>
@@ -839,7 +853,7 @@
     <string name="keyboard_shortcut_a11y_filter_open_apps" msgid="6175417687221004059">"Inaonyesha njia za mkato za kufungua programu"</string>
     <string name="keyboard_shortcut_a11y_filter_current_app" msgid="7944592357493737911">"Inaonyesha njia za mkato za programu unayotumia kwa sasa"</string>
     <string name="group_system_access_notification_shade" msgid="1619028907006553677">"Tazama arifa"</string>
-    <string name="group_system_full_screenshot" msgid="5742204844232667785">"Kupiga picha ya skrini"</string>
+    <string name="group_system_full_screenshot" msgid="5742204844232667785">"Piga picha ya skrini"</string>
     <string name="group_system_access_system_app_shortcuts" msgid="8562482996626694026">"Onyesha njia za mkato"</string>
     <string name="group_system_go_back" msgid="2730322046244918816">"Rudi nyuma"</string>
     <string name="group_system_access_home_screen" msgid="4130366993484706483">"Nenda kwenye skrini ya kwanza"</string>
@@ -1405,12 +1419,11 @@
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Panua aikoni"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"au"</string>
     <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Aikoni ya buruta"</string>
-    <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) -->
-    <skip />
+    <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Mipangilio ya Kibodi"</string>
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Kusogeza kwa kutumia kibodi yako"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Jifunze kuhusu mikato ya kibodi"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Kusogeza kwa kutumia padi yako ya kugusa"</string>
-    <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Jifunze kuhusu miguso ya padi ya kugusa"</string>
+    <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Fahamu miguso ya padi ya kugusa"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Kusogeza kwa kutumia kibodi na padi yako ya kugusa"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Jifunze kuhusu miguso ya padi ya kugusa, mikato ya kibodi na mengineyo"</string>
     <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Rudi nyuma"</string>
@@ -1464,8 +1477,6 @@
     <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Vinavyotolewa na programu"</string>
     <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Maonyesho"</string>
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Visivyojulikana"</string>
-    <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) -->
-    <skip />
-    <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) -->
-    <skip />
+    <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"Badilisha mipangilio ya vigae"</string>
+    <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"Ungependa kurejesha mipangilio chaguomsingi ya ukubwa na mpangilio wa vigae?"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml
index b53c8ec..93b1864 100644
--- a/packages/SystemUI/res/values-ta/strings.xml
+++ b/packages/SystemUI/res/values-ta/strings.xml
@@ -136,11 +136,17 @@
     <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"இப்போது நீங்கள் <xliff:g id="APP_NAME">%1$s</xliff:g> ஐ ரெக்கார்டு செய்கிறீர்கள்"</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"ரெக்கார்டிங்கை நிறுத்து"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"திரையைப் பகிர்கிறது"</string>
+    <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
+    <skip />
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"திரையைப் பகிர்வதை நிறுத்தவா?"</string>
+    <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
+    <skip />
     <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"இப்போது உங்கள் முழுத்திரையையும் <xliff:g id="HOST_APP_NAME">%1$s</xliff:g> உடன் பகிர்கிறீர்கள்"</string>
     <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"இப்போது உங்கள் முழுத்திரையையும் ஆப்ஸுடன் பகிர்கிறீர்கள்"</string>
     <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"இப்போது நீங்கள் <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> ஐப் பகிர்கிறீர்கள்"</string>
     <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"இப்போது நீங்கள் ஓர் ஆப்ஸைப் பகிர்கிறீர்கள்"</string>
+    <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
+    <skip />
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"பகிர்வதை நிறுத்து"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"திரையை அலைபரப்புகிறது"</string>
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"அலைபரப்பை நிறுத்தவா?"</string>
@@ -515,6 +521,10 @@
     <string name="communal_widget_picker_title" msgid="1953369090475731663">"பூட்டுத் திரை விட்ஜெட்கள்"</string>
     <string name="communal_widget_picker_description" msgid="490515450110487871">"டேப்லெட் பூட்டப்பட்டிருந்தாலும் பூட்டுத் திரையில் விட்ஜெட்டை எவரும் பார்க்கலாம்."</string>
     <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"விட்ஜெட்டைத் தேர்வுநீக்கும்"</string>
+    <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
+    <skip />
+    <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
+    <skip />
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"பூட்டுத் திரை விட்ஜெட்கள்"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"விட்ஜெட்டைப் பயன்படுத்தி ஆப்ஸைத் திறக்க, அது நீங்கள்தான் என்பதை உறுதிசெய்ய வேண்டும். அத்துடன், உங்கள் டேப்லெட் பூட்டப்பட்டிருந்தாலும்கூட அவற்றை யார் வேண்டுமானாலும் பார்க்கலாம் என்பதை நினைவில்கொள்ளுங்கள். சில விட்ஜெட்கள் உங்கள் பூட்டுத் திரைக்காக உருவாக்கப்பட்டவை அல்ல என்பதையும் அவற்றை இங்கே சேர்ப்பது பாதுகாப்பற்றதாக இருக்கக்கூடும் என்பதையும் நினைவில்கொள்ளுங்கள்."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"சரி"</string>
@@ -571,6 +581,10 @@
     <string name="clear_all_notifications_text" msgid="348312370303046130">"எல்லாவற்றையும் அழி"</string>
     <string name="manage_notifications_text" msgid="6885645344647733116">"நிர்வகி"</string>
     <string name="manage_notifications_history_text" msgid="57055985396576230">"இதுவரை வந்த அறிவிப்புகள்"</string>
+    <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
+    <skip />
+    <!-- no translation found for notification_history_button_description (1578657591405033383) -->
+    <skip />
     <string name="notification_section_header_incoming" msgid="850925217908095197">"புதிது"</string>
     <string name="notification_section_header_gentle" msgid="6804099527336337197">"சைலன்ட்"</string>
     <string name="notification_section_header_alerting" msgid="5581175033680477651">"அறிவிப்புகள்"</string>
@@ -852,8 +866,8 @@
     <string name="group_system_lock_screen" msgid="7391191300363416543">"பூட்டுத் திரை"</string>
     <string name="group_system_quick_memo" msgid="3764560265935722903">"குறிப்பெடுத்தல்"</string>
     <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"பல வேலைகளைச் செய்தல்"</string>
-    <string name="system_multitasking_rhs" msgid="8714224917276297810">"தற்போது உள்ள ஆப்ஸுடன் வலதுபுறத்தில் திரைப் பிரிப்பைப் பயன்படுத்துதல்"</string>
-    <string name="system_multitasking_lhs" msgid="8402954791206308783">"தற்போது உள்ள ஆப்ஸுடன் இடதுபுறத்தில் திரைப் பிரிப்பைப் பயன்படுத்துதல்"</string>
+    <string name="system_multitasking_rhs" msgid="8714224917276297810">"தற்போது உள்ள ஆப்ஸ் வலதுபுறம் வரும்படி திரைப் பிரிப்பைப் பயன்படுத்துதல்"</string>
+    <string name="system_multitasking_lhs" msgid="8402954791206308783">"தற்போது உள்ள ஆப்ஸ் இடதுபுறம் வரும்படி திரைப் பிரிப்பைப் பயன்படுத்துதல்"</string>
     <string name="system_multitasking_full_screen" msgid="336048080383640562">"திரைப் பிரிப்பு பயன்முறையிலிருந்து முழுத்திரைக்கு மாற்றுதல்"</string>
     <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"திரைப் பிரிப்பைப் பயன்படுத்தும்போது வலது/கீழ் உள்ள ஆப்ஸுக்கு மாறுதல்"</string>
     <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"திரைப் பிரிப்பைப் பயன்படுத்தும்போது இடது/மேலே உள்ள ஆப்ஸுக்கு மாறுதல்"</string>
@@ -1405,8 +1419,7 @@
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"விரிவாக்குவதற்கான ஐகான்"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"அல்லது"</string>
     <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"இழுப்பதற்கான ஹேண்டில்"</string>
-    <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) -->
-    <skip />
+    <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"கீபோர்டு அமைப்புகள்"</string>
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"கீபோர்டைப் பயன்படுத்திச் செல்லுதல்"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"கீபோர்டு ஷார்ட்கட்கள் குறித்துத் தெரிந்துகொள்ளுங்கள்"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"டச்பேடைப் பயன்படுத்திச் செல்லுதல்"</string>
@@ -1464,8 +1477,6 @@
     <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"ஆப்ஸ் வழங்குபவை"</string>
     <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"டிஸ்ப்ளே"</string>
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"தெரியவில்லை"</string>
-    <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) -->
-    <skip />
-    <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) -->
-    <skip />
+    <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"கட்டங்களை மீட்டமைத்தல்"</string>
+    <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"கட்டங்களை அவற்றின் அசல் வரிசைக்கும் அளவுகளுக்கும் மீட்டமைக்கவா?"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml
index d185851..4a97651 100644
--- a/packages/SystemUI/res/values-te/strings.xml
+++ b/packages/SystemUI/res/values-te/strings.xml
@@ -136,11 +136,17 @@
     <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"మీరు ప్రస్తుతం <xliff:g id="APP_NAME">%1$s</xliff:g>‌ను రికార్డ్ చేస్తున్నారు"</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"రికార్డింగ్‌ను ఆపివేయండి"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"స్క్రీన్‌ను షేర్ చేస్తోంది"</string>
+    <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
+    <skip />
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"స్క్రీన్‌ను షేర్ చేయడం ఆపివేయాలా?"</string>
+    <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
+    <skip />
     <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"మీరు ప్రస్తుతం మీ మొత్తం స్క్రీన్‌ను <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>‌తో షేర్ చేస్తున్నారు"</string>
     <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"మీరు ప్రస్తుతం మీ మొత్తం స్క్రీన్‌ను యాప్‌తో షేర్ చేస్తున్నారు"</string>
     <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"మీరు ప్రస్తుతం <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>‌ను షేర్ చేస్తున్నారు"</string>
     <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"మీరు ప్రస్తుతం యాప్‌ను షేర్ చేస్తున్నారు"</string>
+    <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
+    <skip />
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"షేర్ చేయడాన్ని ఆపివేయండి"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"స్క్రీన్‌ను ప్రసారం చేస్తోంది"</string>
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"ప్రసారం చేయడం ఆపివేయాలా?"</string>
@@ -515,6 +521,10 @@
     <string name="communal_widget_picker_title" msgid="1953369090475731663">"లాక్ స్క్రీన్ విడ్జెట్‌లు"</string>
     <string name="communal_widget_picker_description" msgid="490515450110487871">"మీ టాబ్లెట్ లాక్ చేసి ఉన్నా, మీ లాక్ స్క్రీన్‌లో విడ్జెట్‌లను ఎవరైనా చూడవచ్చు."</string>
     <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"విడ్జెట్ ఎంపిక రద్దు చేయండి"</string>
+    <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
+    <skip />
+    <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
+    <skip />
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"లాక్ స్క్రీన్ విడ్జెట్‌లు"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"విడ్జెట్‌ను ఉపయోగించి యాప్‌ను తెరవడానికి, ఇది మీరేనని వెరిఫై చేయాల్సి ఉంటుంది. అలాగే, మీ టాబ్లెట్ లాక్ చేసి ఉన్నప్పటికీ, ఎవరైనా వాటిని చూడగలరని గుర్తుంచుకోండి. కొన్ని విడ్జెట్‌లు మీ లాక్ స్క్రీన్‌కు తగినవి కాకపోవచ్చు, వాటిని ఇక్కడ జోడించడం సురక్షితం కాకపోవచ్చు."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"అర్థమైంది"</string>
@@ -571,6 +581,10 @@
     <string name="clear_all_notifications_text" msgid="348312370303046130">"అన్నీ క్లియర్ చేయండి"</string>
     <string name="manage_notifications_text" msgid="6885645344647733116">"మేనేజ్ చేయండి"</string>
     <string name="manage_notifications_history_text" msgid="57055985396576230">"హిస్టరీ"</string>
+    <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
+    <skip />
+    <!-- no translation found for notification_history_button_description (1578657591405033383) -->
+    <skip />
     <string name="notification_section_header_incoming" msgid="850925217908095197">"కొత్తవి"</string>
     <string name="notification_section_header_gentle" msgid="6804099527336337197">"నిశ్శబ్దం"</string>
     <string name="notification_section_header_alerting" msgid="5581175033680477651">"నోటిఫికేషన్‌లు"</string>
@@ -850,12 +864,12 @@
     <string name="group_system_access_system_settings" msgid="8731721963449070017">"సెట్టింగ్‌లను తెరవండి"</string>
     <string name="group_system_access_google_assistant" msgid="7210074957915968110">"అసిస్టెంట్‌ను తెరవండి"</string>
     <string name="group_system_lock_screen" msgid="7391191300363416543">"లాక్ స్క్రీన్"</string>
-    <string name="group_system_quick_memo" msgid="3764560265935722903">"గమనికను రాయండి"</string>
+    <string name="group_system_quick_memo" msgid="3764560265935722903">"నోట్‌ను రాయండి"</string>
     <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"మల్టీ-టాస్కింగ్"</string>
     <string name="system_multitasking_rhs" msgid="8714224917276297810">"కుడివైపు ప్రస్తుత యాప్‌తో స్ప్లిట్ స్క్రీన్‌ను ఉపయోగించండి"</string>
     <string name="system_multitasking_lhs" msgid="8402954791206308783">"ఎడమవైపు ప్రస్తుత యాప్‌తో స్ప్లిట్ స్క్రీన్‌ను ఉపయోగించండి"</string>
     <string name="system_multitasking_full_screen" msgid="336048080383640562">"స్ప్లిట్ స్క్రీన్‌ను ఫుల్ స్క్రీన్‌కు మార్చండి"</string>
-    <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"స్ప్లిట్ స్క్రీన్ ఉపయోగిస్తున్నప్పుడు కుడి లేదా పైన యాప్‌నకు మారండి"</string>
+    <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"స్ప్లిట్ స్క్రీన్ ఉపయోగిస్తున్నప్పుడు కుడి లేదా కింద యాప్‌నకు మారండి"</string>
     <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"స్ప్లిట్ స్క్రీన్ ఉపయోగిస్తున్నప్పుడు ఎడమ లేదా పైన యాప్‌నకు మారండి"</string>
     <string name="system_multitasking_replace" msgid="7410071959803642125">"స్ప్లిట్ స్క్రీన్ సమయంలో: ఒక దాన్నుండి మరో దానికి యాప్ రీప్లేస్ చేయండి"</string>
     <string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"ఇన్‌పుట్"</string>
@@ -1405,8 +1419,7 @@
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"విస్తరించండి చిహ్నం"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"లేదా"</string>
     <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"లాగే హ్యాండిల్"</string>
-    <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) -->
-    <skip />
+    <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"కీబోర్డ్ సెట్టింగ్‌లు"</string>
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"మీ కీబోర్డ్ ఉపయోగించి నావిగేట్ చేయండి"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"కీబోర్డ్ షార్ట్‌కట్‌ల గురించి తెలుసుకోండి"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"మీ టచ్‌ప్యాడ్‌ని ఉపయోగించి నావిగేట్ చేయండి"</string>
@@ -1464,8 +1477,6 @@
     <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"యాప్‌ల ద్వారా అందించబడినవి"</string>
     <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"డిస్‌ప్లే"</string>
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"తెలియదు"</string>
-    <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) -->
-    <skip />
-    <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) -->
-    <skip />
+    <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"టైల్స్‌ను రీసెట్ చేయండి"</string>
+    <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"టైల్స్‌ను వాటి ఒరిజినల్ క్రమానికి, సైజ్‌లకు రీసెట్ చేయాలా?"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml
index a2a68c1..ac027c7 100644
--- a/packages/SystemUI/res/values-th/strings.xml
+++ b/packages/SystemUI/res/values-th/strings.xml
@@ -136,11 +136,17 @@
     <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"คุณกำลังบันทึก <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"หยุดบันทึก"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"กำลังแชร์หน้าจอ"</string>
+    <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
+    <skip />
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"หยุดแชร์หน้าจอไหม"</string>
+    <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
+    <skip />
     <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"คุณกำลังแชร์ทั้งหน้าจอกับ <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>"</string>
     <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"คุณกำลังแชร์ทั้งหน้าจอกับแอป"</string>
     <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"คุณกำลังแชร์ <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>"</string>
     <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"คุณกำลังแชร์แอป"</string>
+    <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
+    <skip />
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"หยุดแชร์"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"กำลังแคสต์หน้าจอ"</string>
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"หยุดแคสต์ไหม"</string>
@@ -515,6 +521,10 @@
     <string name="communal_widget_picker_title" msgid="1953369090475731663">"วิดเจ็ตในหน้าจอล็อก"</string>
     <string name="communal_widget_picker_description" msgid="490515450110487871">"ทุกคนจะดูวิดเจ็ตที่อยู่ในหน้าจอล็อกของคุณได้ แม้ว่าแท็บเล็ตจะล็อกอยู่ก็ตาม"</string>
     <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"ยกเลิกการเลือกวิดเจ็ต"</string>
+    <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
+    <skip />
+    <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
+    <skip />
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"วิดเจ็ตในหน้าจอล็อก"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"หากต้องการเปิดแอปโดยใช้วิดเจ็ต คุณจะต้องยืนยันตัวตนของคุณ นอกจากนี้ โปรดทราบว่าผู้อื่นจะดูวิดเจ็ตเหล่านี้ได้แม้ว่าแท็บเล็ตจะล็อกอยู่ก็ตาม วิดเจ็ตบางอย่างอาจไม่ได้มีไว้สำหรับหน้าจอล็อกของคุณ และอาจไม่ปลอดภัยที่จะเพิ่มที่นี่"</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"รับทราบ"</string>
@@ -571,6 +581,10 @@
     <string name="clear_all_notifications_text" msgid="348312370303046130">"ล้างทั้งหมด"</string>
     <string name="manage_notifications_text" msgid="6885645344647733116">"จัดการ"</string>
     <string name="manage_notifications_history_text" msgid="57055985396576230">"ประวัติ"</string>
+    <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
+    <skip />
+    <!-- no translation found for notification_history_button_description (1578657591405033383) -->
+    <skip />
     <string name="notification_section_header_incoming" msgid="850925217908095197">"ใหม่"</string>
     <string name="notification_section_header_gentle" msgid="6804099527336337197">"ปิดเสียง"</string>
     <string name="notification_section_header_alerting" msgid="5581175033680477651">"การแจ้งเตือน"</string>
@@ -800,7 +814,7 @@
     <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string>
     <string name="keyboard_key_space" msgid="6980847564173394012">"วรรค"</string>
     <string name="keyboard_key_enter" msgid="8633362970109751646">"Enter"</string>
-    <string name="keyboard_key_backspace" msgid="4095278312039628074">"ลบถอยหลัง"</string>
+    <string name="keyboard_key_backspace" msgid="4095278312039628074">"Backspace"</string>
     <string name="keyboard_key_media_play_pause" msgid="8389984232732277478">"เล่น/หยุดชั่วคราว"</string>
     <string name="keyboard_key_media_stop" msgid="1509943745250377699">"หยุด"</string>
     <string name="keyboard_key_media_next" msgid="8502476691227914952">"ถัดไป"</string>
@@ -1405,8 +1419,7 @@
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"ไอคอนขยาย"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"หรือ"</string>
     <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"แฮนเดิลการลาก"</string>
-    <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) -->
-    <skip />
+    <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"การตั้งค่าแป้นพิมพ์"</string>
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"ไปยังส่วนต่างๆ โดยใช้แป้นพิมพ์"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"ดูข้อมูลเกี่ยวกับแป้นพิมพ์ลัด"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"ไปยังส่วนต่างๆ โดยใช้ทัชแพด"</string>
@@ -1464,8 +1477,6 @@
     <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"ให้บริการโดยแอป"</string>
     <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"จอแสดงผล"</string>
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"ไม่ทราบ"</string>
-    <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) -->
-    <skip />
-    <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) -->
-    <skip />
+    <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"รีเซ็ตการ์ด"</string>
+    <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"รีเซ็ตการ์ดเป็นลำดับและขนาดเดิมไหม"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml
index 4349043..09a0e040 100644
--- a/packages/SystemUI/res/values-tl/strings.xml
+++ b/packages/SystemUI/res/values-tl/strings.xml
@@ -136,11 +136,17 @@
     <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Kasalukuyan mong nire-record ang <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Huminto sa pag-record"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Ibinabahagi ang screen"</string>
+    <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
+    <skip />
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Ihinto ang pagbabahagi ng screen?"</string>
+    <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
+    <skip />
     <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Kasalukuyan mong ibinabahagi ang iyong buong screen sa <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>"</string>
     <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Kasalukuyan mong ibinabahagi ang iyong buong screen sa isang app"</string>
     <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"Kasalukuyan kang nagbabahagi ng <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>"</string>
     <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"Kasalukuyan kang nagbabahagi ng app"</string>
+    <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
+    <skip />
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Ihinto ang pagbabahagi"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Kina-cast ang screen"</string>
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Ihinto ang pag-cast?"</string>
@@ -515,6 +521,10 @@
     <string name="communal_widget_picker_title" msgid="1953369090475731663">"Mga widget ng lock screen"</string>
     <string name="communal_widget_picker_description" msgid="490515450110487871">"Makikita ng sinuman ang mga widget sa lock screen, kahit naka-lock ang tablet."</string>
     <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"i-unselect ang widget"</string>
+    <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
+    <skip />
+    <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
+    <skip />
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Mga widget ng lock screen"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Para magbukas ng app gamit ang isang widget, kakailanganin mong i-verify na ikaw iyan. Bukod pa rito, tandaang puwedeng tingnan ng kahit na sino ang mga ito, kahit na naka-lock ang iyong tablet. Posibleng hindi para sa iyong lock screen ang ilang widget at posibleng hindi ligtas ang mga ito na idagdag dito."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"OK"</string>
@@ -571,6 +581,10 @@
     <string name="clear_all_notifications_text" msgid="348312370303046130">"I-clear lahat"</string>
     <string name="manage_notifications_text" msgid="6885645344647733116">"Pamahalaan"</string>
     <string name="manage_notifications_history_text" msgid="57055985396576230">"History"</string>
+    <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
+    <skip />
+    <!-- no translation found for notification_history_button_description (1578657591405033383) -->
+    <skip />
     <string name="notification_section_header_incoming" msgid="850925217908095197">"Bago"</string>
     <string name="notification_section_header_gentle" msgid="6804099527336337197">"Naka-silent"</string>
     <string name="notification_section_header_alerting" msgid="5581175033680477651">"Mga Notification"</string>
@@ -1405,8 +1419,7 @@
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"I-expand ang icon"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"o"</string>
     <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Handle sa pag-drag"</string>
-    <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) -->
-    <skip />
+    <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Mga Setting ng Keyboard"</string>
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Mag-navigate gamit ang iyong keyboard"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Matuto ng mga keyboard shortcut"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Mag-navigate gamit ang iyong touchpad"</string>
@@ -1464,8 +1477,6 @@
     <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Ibinibigay ng mga app"</string>
     <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Display"</string>
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Hindi Alam"</string>
-    <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) -->
-    <skip />
-    <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) -->
-    <skip />
+    <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"I-reset ang mga tile"</string>
+    <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"I-reset ang mga tile sa orihinal na pagkakasunod-sunod at mga laki ng mga ito?"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index cf892e0..ae9cd18 100644
--- a/packages/SystemUI/res/values-tr/strings.xml
+++ b/packages/SystemUI/res/values-tr/strings.xml
@@ -136,11 +136,17 @@
     <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Şu anda <xliff:g id="APP_NAME">%1$s</xliff:g> içeriğini kaydediyorsunuz"</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Kaydı durdur"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Ekran paylaşılıyor"</string>
+    <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
+    <skip />
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Ekran paylaşımı durdurulsun mu?"</string>
+    <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
+    <skip />
     <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Şu anda ekranınızın tamamını <xliff:g id="HOST_APP_NAME">%1$s</xliff:g> ile paylaşıyorsunuz"</string>
     <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Şu anda ekranınızın tamamını bir uygulamayla paylaşıyorsunuz"</string>
     <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"Şu anda <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> içeriğini paylaşıyorsunuz"</string>
     <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"Şu anda bir uygulamayı paylaşıyorsunuz"</string>
+    <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
+    <skip />
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Paylaşımı durdur"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Ekran yayınlanıyor"</string>
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Yayın durdurulsun mu?"</string>
@@ -515,6 +521,10 @@
     <string name="communal_widget_picker_title" msgid="1953369090475731663">"Kilit ekranı widget\'ları"</string>
     <string name="communal_widget_picker_description" msgid="490515450110487871">"Kilit ekranınızdaki widget\'lar, tabletiniz kilitliyken bile herkes tarafından görüntülenebilir."</string>
     <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"widget\'ın seçimini kaldırın"</string>
+    <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
+    <skip />
+    <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
+    <skip />
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Kilit ekranı widget\'ları"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Widget kullanarak bir uygulamayı açmak için kimliğinizi doğrulamanız gerekir. Ayrıca, tabletiniz kilitliyken bile widget\'ların herkes tarafından görüntülenebileceğini unutmayın. Bazı widget\'lar kilit ekranınız için tasarlanmamış olabileceğinden buraya eklenmeleri güvenli olmayabilir."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Anladım"</string>
@@ -571,6 +581,10 @@
     <string name="clear_all_notifications_text" msgid="348312370303046130">"Tümünü temizle"</string>
     <string name="manage_notifications_text" msgid="6885645344647733116">"Yönet"</string>
     <string name="manage_notifications_history_text" msgid="57055985396576230">"Geçmiş"</string>
+    <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
+    <skip />
+    <!-- no translation found for notification_history_button_description (1578657591405033383) -->
+    <skip />
     <string name="notification_section_header_incoming" msgid="850925217908095197">"Yeni"</string>
     <string name="notification_section_header_gentle" msgid="6804099527336337197">"Sessiz"</string>
     <string name="notification_section_header_alerting" msgid="5581175033680477651">"Bildirimler"</string>
@@ -1391,7 +1405,7 @@
     <string name="shortcut_helper_category_system" msgid="462110876978937359">"Sistem"</string>
     <string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"Sistem kontrolleri"</string>
     <string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Sistem uygulamaları"</string>
-    <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Çoklu görev becerisi"</string>
+    <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Çoklu görev"</string>
     <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"Son uygulamalar"</string>
     <string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Bölünmüş ekran"</string>
     <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Giriş"</string>
@@ -1405,8 +1419,7 @@
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Genişlet simgesi"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"veya"</string>
     <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Sürükleme tutamacı"</string>
-    <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) -->
-    <skip />
+    <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Klavye Ayarları"</string>
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Klavyenizi kullanarak gezinin"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Klavye kısayollarını öğrenin"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Dokunmatik alanınızı kullanarak gezinin"</string>
@@ -1464,8 +1477,6 @@
     <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Uygulamalar tarafından sağlanır"</string>
     <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Ekran"</string>
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Bilinmiyor"</string>
-    <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) -->
-    <skip />
-    <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) -->
-    <skip />
+    <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"Kartları sıfırla"</string>
+    <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"Kartlar orijinal sıralarına ve boyutlarına sıfırlansın mı?"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml
index 307fc27..820d072 100644
--- a/packages/SystemUI/res/values-uk/strings.xml
+++ b/packages/SystemUI/res/values-uk/strings.xml
@@ -136,11 +136,17 @@
     <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Ви зараз записуєте вміст екрана додатка <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Зупинити запис"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Показ екрана"</string>
+    <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
+    <skip />
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Зупинити показ екрана?"</string>
+    <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
+    <skip />
     <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Ви зараз показуєте вміст усього екрана в додатку <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>"</string>
     <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Ви зараз показуєте вміст усього екрана в додатку."</string>
     <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"Ви зараз показуєте вікно додатка <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>"</string>
     <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"Ви зараз показуєте вікно додатка"</string>
+    <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
+    <skip />
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Зупинити показ"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Трансляція екрана"</string>
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Зупинити трансляцію?"</string>
@@ -515,6 +521,10 @@
     <string name="communal_widget_picker_title" msgid="1953369090475731663">"Віджети для заблокованого екрана"</string>
     <string name="communal_widget_picker_description" msgid="490515450110487871">"Будь-хто бачитиме віджети навіть на заблокованому екрані планшета."</string>
     <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"скасувати вибір віджета"</string>
+    <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
+    <skip />
+    <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
+    <skip />
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Віджети для заблокованого екрана"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Щоб відкрити додаток за допомогою віджета, вам потрібно буде підтвердити особу. Пам’ятайте також, що бачити віджети можуть усі, навіть коли планшет заблоковано. Можливо, деякі віджети не призначені для заблокованого екрана, і додавати їх на нього може бути небезпечно."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"OK"</string>
@@ -571,6 +581,10 @@
     <string name="clear_all_notifications_text" msgid="348312370303046130">"Очистити все"</string>
     <string name="manage_notifications_text" msgid="6885645344647733116">"Керувати"</string>
     <string name="manage_notifications_history_text" msgid="57055985396576230">"Історія"</string>
+    <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
+    <skip />
+    <!-- no translation found for notification_history_button_description (1578657591405033383) -->
+    <skip />
     <string name="notification_section_header_incoming" msgid="850925217908095197">"Нові"</string>
     <string name="notification_section_header_gentle" msgid="6804099527336337197">"Без звуку"</string>
     <string name="notification_section_header_alerting" msgid="5581175033680477651">"Сповіщення"</string>
@@ -1405,8 +1419,7 @@
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Значок розгортання"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"або"</string>
     <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Маркер переміщення"</string>
-    <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) -->
-    <skip />
+    <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Налаштування клавіатури"</string>
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Навігація за допомогою клавіатури"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Комбінації клавіш: докладніше"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Навігація за допомогою сенсорної панелі"</string>
@@ -1464,8 +1477,6 @@
     <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Надано додатками"</string>
     <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Екран"</string>
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Невідомо"</string>
-    <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) -->
-    <skip />
-    <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) -->
-    <skip />
+    <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"Скинути панелі"</string>
+    <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"Відновити початковий порядок і розмір панелей?"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml
index 4558486..b98abbd 100644
--- a/packages/SystemUI/res/values-ur/strings.xml
+++ b/packages/SystemUI/res/values-ur/strings.xml
@@ -136,11 +136,17 @@
     <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"آپ فی الحال <xliff:g id="APP_NAME">%1$s</xliff:g> ریکارڈ کر رہے ہیں"</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"ریکارڈنگ روکیں"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"اسکرین کا اشتراک ہو رہا ہے"</string>
+    <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
+    <skip />
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"اسکرین کا اشتراک روکیں؟"</string>
+    <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
+    <skip />
     <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"آپ فی الحال <xliff:g id="HOST_APP_NAME">%1$s</xliff:g> کے ساتھ اپنی پوری اسکرین کا اشتراک کر رہے ہیں"</string>
     <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"آپ فی الحال ایک ایپ کے ساتھ اپنی پوری اسکرین کا اشتراک کر رہے ہیں"</string>
     <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"آپ فی الحال <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> کا اشتراک کر رہے ہیں"</string>
     <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"آپ فی الحال ایک ایپ کا اشتراک کر رہے ہیں"</string>
+    <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
+    <skip />
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"اشتراک کرنا روکیں"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"اسکرین کاسٹ ہو رہی ہے"</string>
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"کاسٹ کرنا بند کریں؟"</string>
@@ -515,6 +521,10 @@
     <string name="communal_widget_picker_title" msgid="1953369090475731663">"مقفل اسکرین کے ویجیٹس"</string>
     <string name="communal_widget_picker_description" msgid="490515450110487871">"کوئی بھی آپ کی مقفل اسکرین پر ویجیٹ دیکھ سکتا ہے اگرچہ آپ کا ٹیبلیٹ مقفل ہو۔"</string>
     <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"ویجیٹ غیر منتخب کریں"</string>
+    <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
+    <skip />
+    <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
+    <skip />
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"مقفل اسکرین کے ویجیٹس"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"ویجیٹ کے ذریعے ایپ کھولنے کے لیے آپ کو تصدیق کرنی ہوگی کہ یہ آپ ہی ہیں۔ نیز، ذہن میں رکھیں کہ کوئی بھی انہیں دیکھ سکتا ہے، یہاں تک کہ جب آپ کا ٹیبلیٹ مقفل ہو۔ ہو سکتا ہے کچھ ویجٹس آپ کی لاک اسکرین کے لیے نہ بنائے گئے ہوں اور یہاں شامل کرنا غیر محفوظ ہو سکتا ہے۔"</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"سمجھ آ گئی"</string>
@@ -571,6 +581,10 @@
     <string name="clear_all_notifications_text" msgid="348312370303046130">"سبھی کو صاف کریں"</string>
     <string name="manage_notifications_text" msgid="6885645344647733116">"نظم کریں"</string>
     <string name="manage_notifications_history_text" msgid="57055985396576230">"سرگزشت"</string>
+    <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
+    <skip />
+    <!-- no translation found for notification_history_button_description (1578657591405033383) -->
+    <skip />
     <string name="notification_section_header_incoming" msgid="850925217908095197">"نیا"</string>
     <string name="notification_section_header_gentle" msgid="6804099527336337197">"خاموش"</string>
     <string name="notification_section_header_alerting" msgid="5581175033680477651">"اطلاعات"</string>
@@ -844,8 +858,8 @@
     <string name="group_system_go_back" msgid="2730322046244918816">"واپس جائیں"</string>
     <string name="group_system_access_home_screen" msgid="4130366993484706483">"ہوم اسکرین پر جائیں"</string>
     <string name="group_system_overview_open_apps" msgid="5659958952937994104">"حالیہ ایپس دیکھیں"</string>
-    <string name="group_system_cycle_forward" msgid="5478663965957647805">"حالیہ ایپس کے ذریعے آگے کی طرف سائیکل کریں"</string>
-    <string name="group_system_cycle_back" msgid="8194102916946802902">"حالیہ ایپس کے ذریعے پیچھے کی طرف سائیکل کریں"</string>
+    <string name="group_system_cycle_forward" msgid="5478663965957647805">"حالیہ ایپس میں اگلی ایپ پر جائیں"</string>
+    <string name="group_system_cycle_back" msgid="8194102916946802902">"حالیہ ایپس میں پچھلی ایپ پر جائیں"</string>
     <string name="group_system_access_all_apps_search" msgid="1553588630154197469">"ایپس کی فہرست کھولیں"</string>
     <string name="group_system_access_system_settings" msgid="8731721963449070017">"ترتیبات کھولیں"</string>
     <string name="group_system_access_google_assistant" msgid="7210074957915968110">"اسسٹنٹ کھولیں"</string>
@@ -1405,8 +1419,7 @@
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"آئیکن پھیلائیں"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"یا"</string>
     <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"گھسیٹنے کا ہینڈل"</string>
-    <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) -->
-    <skip />
+    <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"کی بورڈ کی ترتیبات"</string>
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"اپنے کی بورڈ کا استعمال کر کے نیویگیٹ کریں"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"کی بورڈ شارٹ کٹس جانیں"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"اپنے ٹچ پیڈ کا استعمال کر کے نیویگیٹ کریں"</string>
@@ -1464,8 +1477,6 @@
     <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"ایپس کے ذریعہ فراہم کردہ"</string>
     <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"ڈسپلے"</string>
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"نامعلوم"</string>
-    <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) -->
-    <skip />
-    <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) -->
-    <skip />
+    <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"ٹائلز ری سیٹ کریں"</string>
+    <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"ٹائلز کو ان کے اصل آرڈر اور سائزز پر ری سیٹ کریں؟"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-uz/strings.xml b/packages/SystemUI/res/values-uz/strings.xml
index 00c97b1..3309772 100644
--- a/packages/SystemUI/res/values-uz/strings.xml
+++ b/packages/SystemUI/res/values-uz/strings.xml
@@ -136,11 +136,17 @@
     <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Hozir <xliff:g id="APP_NAME">%1$s</xliff:g> ilovasi yozib olinmoqda"</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Yozuvni toʻxtatish"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Ekran ulashilmoqda"</string>
+    <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
+    <skip />
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Ekran namoyishi toʻxtatilsinmi?"</string>
+    <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
+    <skip />
     <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Hozir butun ekran <xliff:g id="HOST_APP_NAME">%1$s</xliff:g> ilovasiga ulashilmoqda"</string>
     <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Hozir butun ekran ilovaga ulashilmoqda"</string>
     <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"Hozir <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> ilovasiga kontent ulashilmoqda"</string>
     <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"Hozir ilovaga kontent ulashilmoqda"</string>
+    <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
+    <skip />
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Namoyishni toʻxtatish"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Ekran translatsiya qilinmoqda"</string>
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Toʻxtatilsinmi?"</string>
@@ -515,6 +521,10 @@
     <string name="communal_widget_picker_title" msgid="1953369090475731663">"Ekran qulfi vidjetlari"</string>
     <string name="communal_widget_picker_description" msgid="490515450110487871">"Ekran quflidagi vidjetlar hammaga koʻrinadi, hatto planshet qulflanganda ham."</string>
     <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"vidjetni bekor qilish"</string>
+    <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
+    <skip />
+    <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
+    <skip />
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Ekran qulfi vidjetlari"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Ilovani vidjet orqali ochish uchun shaxsingizni tasdiqlashingiz kerak. Shuningdek, planshet qulflanganda ham bu axborotlar hammaga koʻrinishini unutmang. Ayrim vidjetlar ekran qulfiga moslanmagan va ularni bu yerda chiqarish xavfli boʻlishi mumkin."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"OK"</string>
@@ -571,6 +581,10 @@
     <string name="clear_all_notifications_text" msgid="348312370303046130">"Hammasini tozalash"</string>
     <string name="manage_notifications_text" msgid="6885645344647733116">"Boshqarish"</string>
     <string name="manage_notifications_history_text" msgid="57055985396576230">"Tarix"</string>
+    <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
+    <skip />
+    <!-- no translation found for notification_history_button_description (1578657591405033383) -->
+    <skip />
     <string name="notification_section_header_incoming" msgid="850925217908095197">"Yangi"</string>
     <string name="notification_section_header_gentle" msgid="6804099527336337197">"Sokin"</string>
     <string name="notification_section_header_alerting" msgid="5581175033680477651">"Bildirishnomalar"</string>
@@ -1405,8 +1419,7 @@
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Yoyish belgisi"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"yoki"</string>
     <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Surish dastagi"</string>
-    <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) -->
-    <skip />
+    <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Klaviatura sozlamalari"</string>
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Klaviatura yordamida kezing"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Tezkor tugmalar haqida"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Sensorli panel yordamida kezing"</string>
diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml
index 6145f7c..f404a2b 100644
--- a/packages/SystemUI/res/values-vi/strings.xml
+++ b/packages/SystemUI/res/values-vi/strings.xml
@@ -136,11 +136,17 @@
     <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Bạn đang ghi lại nội dung của <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Dừng ghi"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Đang chia sẻ màn hình"</string>
+    <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
+    <skip />
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Dừng chia sẻ màn hình?"</string>
+    <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
+    <skip />
     <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Bạn đang chia sẻ toàn bộ nội dung trên màn hình với <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>"</string>
     <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Bạn đang chia sẻ toàn bộ nội dung trên màn hình với một ứng dụng"</string>
     <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"Bạn đang chia sẻ nội dung của <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>"</string>
     <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"Bạn đang chia sẻ một ứng dụng"</string>
+    <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
+    <skip />
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Dừng chia sẻ"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Đang truyền màn hình"</string>
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Dừng truyền?"</string>
@@ -515,6 +521,10 @@
     <string name="communal_widget_picker_title" msgid="1953369090475731663">"Tiện ích trên màn hình khoá"</string>
     <string name="communal_widget_picker_description" msgid="490515450110487871">"Ai cũng thấy được tiện ích trên màn hình khoá, kể cả khi khoá máy tính bảng."</string>
     <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"bỏ chọn tiện ích"</string>
+    <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
+    <skip />
+    <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
+    <skip />
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Tiện ích trên màn hình khoá"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Để dùng tiện ích mở một ứng dụng, bạn cần xác minh danh tính của mình. Ngoài ra, hãy lưu ý rằng bất kỳ ai cũng có thể xem các tiện ích này, ngay cả khi máy tính bảng của bạn được khoá. Một số tiện ích có thể không dành cho màn hình khoá và không an toàn khi thêm vào đây."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Tôi hiểu"</string>
@@ -571,6 +581,10 @@
     <string name="clear_all_notifications_text" msgid="348312370303046130">"Xóa tất cả"</string>
     <string name="manage_notifications_text" msgid="6885645344647733116">"Quản lý"</string>
     <string name="manage_notifications_history_text" msgid="57055985396576230">"Lịch sử"</string>
+    <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
+    <skip />
+    <!-- no translation found for notification_history_button_description (1578657591405033383) -->
+    <skip />
     <string name="notification_section_header_incoming" msgid="850925217908095197">"Mới"</string>
     <string name="notification_section_header_gentle" msgid="6804099527336337197">"Im lặng"</string>
     <string name="notification_section_header_alerting" msgid="5581175033680477651">"Thông báo"</string>
@@ -1405,8 +1419,7 @@
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Biểu tượng Mở rộng"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"hoặc"</string>
     <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Nút kéo"</string>
-    <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) -->
-    <skip />
+    <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Cài đặt bàn phím"</string>
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Di chuyển bằng bàn phím"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Tìm hiểu về phím tắt"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Di chuyển bằng bàn di chuột"</string>
@@ -1464,8 +1477,6 @@
     <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Do các ứng dụng cung cấp"</string>
     <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Hiển thị"</string>
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Không xác định"</string>
-    <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) -->
-    <skip />
-    <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) -->
-    <skip />
+    <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"Đặt lại các ô"</string>
+    <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"Đặt lại các ô về thứ tự và kích thước ban đầu?"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index 53ba179..24c9f40 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -136,11 +136,17 @@
     <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"您正在录制“<xliff:g id="APP_NAME">%1$s</xliff:g>”"</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"停止录制"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"正在共享屏幕"</string>
+    <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
+    <skip />
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"要停止共享屏幕吗?"</string>
+    <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
+    <skip />
     <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"您正在与“<xliff:g id="HOST_APP_NAME">%1$s</xliff:g>”分享整个屏幕"</string>
     <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"您正在与一个应用分享整个屏幕"</string>
     <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"您正在分享“<xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>”的画面"</string>
     <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"您正在分享一个应用的画面"</string>
+    <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
+    <skip />
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"停止共享"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"正在投屏"</string>
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"停止投屏吗?"</string>
@@ -515,6 +521,10 @@
     <string name="communal_widget_picker_title" msgid="1953369090475731663">"锁屏微件"</string>
     <string name="communal_widget_picker_description" msgid="490515450110487871">"任何人都可以查看锁屏上的微件,平板电脑处于锁定状态时也是如此。"</string>
     <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"取消选中微件"</string>
+    <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
+    <skip />
+    <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
+    <skip />
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"锁屏微件"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"若要使用微件打开应用,您需要验证是您本人在操作。另外请注意,任何人都可以查看此类微件,即使您的平板电脑已锁定。有些微件可能不适合显示在锁定的屏幕中,因此添加到这里可能不安全。"</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"知道了"</string>
@@ -571,6 +581,10 @@
     <string name="clear_all_notifications_text" msgid="348312370303046130">"全部清除"</string>
     <string name="manage_notifications_text" msgid="6885645344647733116">"管理"</string>
     <string name="manage_notifications_history_text" msgid="57055985396576230">"历史记录"</string>
+    <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
+    <skip />
+    <!-- no translation found for notification_history_button_description (1578657591405033383) -->
+    <skip />
     <string name="notification_section_header_incoming" msgid="850925217908095197">"最新"</string>
     <string name="notification_section_header_gentle" msgid="6804099527336337197">"静音"</string>
     <string name="notification_section_header_alerting" msgid="5581175033680477651">"通知"</string>
@@ -852,8 +866,8 @@
     <string name="group_system_lock_screen" msgid="7391191300363416543">"锁定屏幕"</string>
     <string name="group_system_quick_memo" msgid="3764560265935722903">"添加记事"</string>
     <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"多任务处理"</string>
-    <string name="system_multitasking_rhs" msgid="8714224917276297810">"使用分屏模式,并且当前应用位于右侧"</string>
-    <string name="system_multitasking_lhs" msgid="8402954791206308783">"使用分屏模式,并且当前应用位于左侧"</string>
+    <string name="system_multitasking_rhs" msgid="8714224917276297810">"使用分屏模式,并将当前应用置于右侧"</string>
+    <string name="system_multitasking_lhs" msgid="8402954791206308783">"使用分屏模式,并将当前应用置于左侧"</string>
     <string name="system_multitasking_full_screen" msgid="336048080383640562">"从分屏模式切换为全屏"</string>
     <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"使用分屏模式时,切换到右侧或下方的应用"</string>
     <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"使用分屏模式时,切换到左侧或上方的应用"</string>
@@ -1405,8 +1419,7 @@
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"展开图标"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"或"</string>
     <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"拖动手柄"</string>
-    <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) -->
-    <skip />
+    <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"键盘设置"</string>
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"使用键盘导航"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"了解键盘快捷键"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"使用触控板导航"</string>
@@ -1464,8 +1477,6 @@
     <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"由应用提供"</string>
     <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"显示"</string>
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"未知"</string>
-    <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) -->
-    <skip />
-    <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) -->
-    <skip />
+    <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"重置功能块"</string>
+    <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"要将功能块重置为原始排序和大小吗?"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml
index f496c41..e7ebe5c 100644
--- a/packages/SystemUI/res/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/strings.xml
@@ -136,11 +136,17 @@
     <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"你正在錄影「<xliff:g id="APP_NAME">%1$s</xliff:g>」"</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"停止錄製"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"正在分享螢幕"</string>
+    <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
+    <skip />
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"要停止分享螢幕嗎?"</string>
+    <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
+    <skip />
     <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"你正與「<xliff:g id="HOST_APP_NAME">%1$s</xliff:g>」分享整個螢幕畫面"</string>
     <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"你正與一個應用程式分享整個螢幕畫面"</string>
     <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"你正在分享「<xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>」"</string>
     <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"你正在分享一個應用程式"</string>
+    <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
+    <skip />
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"停止分享"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"正在投放螢幕"</string>
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"要停止投放嗎?"</string>
@@ -515,6 +521,10 @@
     <string name="communal_widget_picker_title" msgid="1953369090475731663">"上鎖畫面小工具"</string>
     <string name="communal_widget_picker_description" msgid="490515450110487871">"無論平板電腦的螢幕是否已上鎖,任何人都可以看到上鎖畫面小工具。"</string>
     <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"取消揀小工具"</string>
+    <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
+    <skip />
+    <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
+    <skip />
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"上鎖畫面小工具"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"如要使用小工具開啟應用程式,系統會要求你驗證身分。請注意,所有人都能查看小工具,即使平板電腦已鎖定亦然。部分小工具可能不適用於上鎖畫面,新增至這裡可能會有安全疑慮。"</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"知道了"</string>
@@ -571,6 +581,10 @@
     <string name="clear_all_notifications_text" msgid="348312370303046130">"全部清除"</string>
     <string name="manage_notifications_text" msgid="6885645344647733116">"管理"</string>
     <string name="manage_notifications_history_text" msgid="57055985396576230">"記錄"</string>
+    <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
+    <skip />
+    <!-- no translation found for notification_history_button_description (1578657591405033383) -->
+    <skip />
     <string name="notification_section_header_incoming" msgid="850925217908095197">"新"</string>
     <string name="notification_section_header_gentle" msgid="6804099527336337197">"靜音"</string>
     <string name="notification_section_header_alerting" msgid="5581175033680477651">"通知"</string>
@@ -1405,8 +1419,7 @@
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"展開圖示"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"或"</string>
     <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"拖曳控點"</string>
-    <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) -->
-    <skip />
+    <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"鍵盤設定"</string>
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"使用鍵盤導覽"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"瞭解鍵盤快速鍵"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"使用觸控板導覽"</string>
@@ -1464,8 +1477,6 @@
     <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"由應用程式提供"</string>
     <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"螢幕"</string>
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"不明"</string>
-    <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) -->
-    <skip />
-    <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) -->
-    <skip />
+    <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"重設圖塊"</string>
+    <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"要重設圖塊的順序和大小嗎?"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index 5157809..0f298dc 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -136,11 +136,17 @@
     <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"目前正在錄製「<xliff:g id="APP_NAME">%1$s</xliff:g>」的畫面"</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"停止錄製"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"正在分享畫面"</string>
+    <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
+    <skip />
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"停止分享?"</string>
+    <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
+    <skip />
     <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"目前正在與「<xliff:g id="HOST_APP_NAME">%1$s</xliff:g>」分享整個畫面"</string>
     <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"目前正在與某個應用程式分享整個畫面"</string>
     <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"目前正在分享「<xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>」的畫面"</string>
     <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"目前正在分享應用程式畫面"</string>
+    <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
+    <skip />
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"停止分享"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"正在投放畫面"</string>
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"停止投放?"</string>
@@ -515,6 +521,10 @@
     <string name="communal_widget_picker_title" msgid="1953369090475731663">"螢幕鎖定小工具"</string>
     <string name="communal_widget_picker_description" msgid="490515450110487871">"即使平板電腦已鎖定,所有人仍可查看螢幕鎖定畫面上的小工具。"</string>
     <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"取消選取小工具"</string>
+    <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
+    <skip />
+    <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
+    <skip />
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"螢幕鎖定小工具"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"如要使用小工具開啟應用程式,需先驗證身分。請留意,即使平板電腦已鎖定,所有人都還是能查看小工具。某些小工具可能不適用於螢幕鎖定畫面,新增到此可能會有安全疑慮。"</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"我知道了"</string>
@@ -571,6 +581,10 @@
     <string name="clear_all_notifications_text" msgid="348312370303046130">"全部清除"</string>
     <string name="manage_notifications_text" msgid="6885645344647733116">"管理"</string>
     <string name="manage_notifications_history_text" msgid="57055985396576230">"記錄"</string>
+    <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
+    <skip />
+    <!-- no translation found for notification_history_button_description (1578657591405033383) -->
+    <skip />
     <string name="notification_section_header_incoming" msgid="850925217908095197">"最新"</string>
     <string name="notification_section_header_gentle" msgid="6804099527336337197">"靜音"</string>
     <string name="notification_section_header_alerting" msgid="5581175033680477651">"通知"</string>
@@ -1405,8 +1419,7 @@
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"展開圖示"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"或"</string>
     <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"拖曳控點"</string>
-    <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) -->
-    <skip />
+    <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"鍵盤設定"</string>
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"使用鍵盤操作"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"學習鍵盤快速鍵"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"使用觸控板操作"</string>
@@ -1464,8 +1477,6 @@
     <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"由應用程式提供"</string>
     <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"螢幕"</string>
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"不明"</string>
-    <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) -->
-    <skip />
-    <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) -->
-    <skip />
+    <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"重設設定方塊"</string>
+    <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"要將設定方塊的順序和大小恢復預設值嗎?"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml
index cd577e5..444ca04 100644
--- a/packages/SystemUI/res/values-zu/strings.xml
+++ b/packages/SystemUI/res/values-zu/strings.xml
@@ -136,11 +136,17 @@
     <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Njengamanje urekhoda i-<xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Misa ukurekhoda"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Yabelana ngesikrini"</string>
+    <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
+    <skip />
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Misa ukwabelana ngeskrini?"</string>
+    <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
+    <skip />
     <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Njengamanje wabelana ngaso sonke isikrini sakho ne-<xliff:g id="HOST_APP_NAME">%1$s</xliff:g>"</string>
     <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Njengamanje wabelana ngaso sonke isikrini sakho ne-app"</string>
     <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"Njengamanje wabelana nge-<xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>"</string>
     <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"Njengamanje wabelana nge-app"</string>
+    <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
+    <skip />
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Misa ukwabelana"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Isikrini sokusakaza"</string>
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Misa ukusakaza?"</string>
@@ -515,6 +521,10 @@
     <string name="communal_widget_picker_title" msgid="1953369090475731663">"Amawijethi wesikrini esikhiyiwe"</string>
     <string name="communal_widget_picker_description" msgid="490515450110487871">"Noma ubani angabuka amawijethi ngisho noma ithebulethi ikhiyiwe."</string>
     <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"yeka ukukhetha iwijethi"</string>
+    <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
+    <skip />
+    <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
+    <skip />
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Amawijethi wesikrini esikhiyiwe"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Ukuze uvule i-app usebenzisa iwijethi, uzodinga ukuqinisekisa ukuthi nguwe. Futhi, khumbula ukuthi noma ubani angakwazi ukuzibuka, nanoma ithebhulethi yakho ikhiyiwe. Amanye amawijethi kungenzeka abengahloselwe ukukhiya isikrini sakho futhi kungenzeka awaphephile ukuthi angafakwa lapha."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Ngiyezwa"</string>
@@ -571,6 +581,10 @@
     <string name="clear_all_notifications_text" msgid="348312370303046130">"Sula konke"</string>
     <string name="manage_notifications_text" msgid="6885645344647733116">"Phatha"</string>
     <string name="manage_notifications_history_text" msgid="57055985396576230">"Umlando"</string>
+    <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
+    <skip />
+    <!-- no translation found for notification_history_button_description (1578657591405033383) -->
+    <skip />
     <string name="notification_section_header_incoming" msgid="850925217908095197">"Okusha"</string>
     <string name="notification_section_header_gentle" msgid="6804099527336337197">"Kuthulile"</string>
     <string name="notification_section_header_alerting" msgid="5581175033680477651">"Izaziso"</string>
@@ -1405,8 +1419,7 @@
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Nweba isithonjana"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"noma"</string>
     <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Hudula isibambi"</string>
-    <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) -->
-    <skip />
+    <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Amasethingi Ekhibhodi"</string>
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Funa usebenzisa ikhibhodi yakho"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Funda izinqamuleli zamakhibhodi"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Funa usebenzisa iphedi yokuthinta"</string>
@@ -1464,8 +1477,6 @@
     <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Kuhlinzekwe ama-app"</string>
     <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Bonisa"</string>
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Akwaziwa"</string>
-    <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) -->
-    <skip />
-    <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) -->
-    <skip />
+    <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"Setha amathayela kabusha"</string>
+    <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"Setha kabusha amathayela ekuhlelekeni nakosayizi bawo bangempela?"</string>
 </resources>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 87e6aa0..c2d942f 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -2064,5 +2064,11 @@
 
     <dimen name="volume_panel_slice_vertical_padding">8dp</dimen>
     <dimen name="volume_panel_slice_horizontal_padding">24dp</dimen>
+
+    <dimen name="volume_dialog_ringer_container_padding">10dp</dimen>
+    <dimen name="volume_ringer_item_size">40dp</dimen>
+    <dimen name="volume_ringer_icon_size">20dp</dimen>
+    <dimen name="volume_ringer_item_radius">12dp</dimen>
+    <dimen name="volume_dialog_ringer_item_margin_bottom">8dp</dimen>
     <!-- Volume end -->
 </resources>
diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml
index 20e70e0..88ed4e3 100644
--- a/packages/SystemUI/res/values/ids.xml
+++ b/packages/SystemUI/res/values/ids.xml
@@ -286,4 +286,6 @@
     <item type="id" name="snapshot_view_binding" />
     <item type="id" name="snapshot_view_binding_root" />
 
+    <item type="id" name="brightness_dialog_slider" />
+
 </resources>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index c494e85..9602163 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -289,7 +289,8 @@
     <!-- Screen recording permission option for recording just a single app [CHAR LIMIT=50] -->
     <string name="screenrecord_permission_dialog_option_text_single_app">Record one app</string>
     <!-- Screen recording permission option for recording the whole screen [CHAR LIMIT=50] -->
-    <string name="screenrecord_permission_dialog_option_text_entire_screen">Record entire screen</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" >Record entire screen</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display">Record entire screen: %s</string>
     <!-- Message reminding the user that sensitive information may be captured during a full screen recording for the updated dialog that includes partial screen sharing option [CHAR_LIMIT=350]-->
     <string name="screenrecord_permission_dialog_warning_entire_screen">When you’re recording your entire screen, anything shown on your screen is recorded. So be careful with things like passwords, payment details, messages, photos, and audio and video.</string>
     <!-- Message reminding the user that sensitive information may be captured during a single app screen recording for the updated dialog that includes partial screen sharing option [CHAR_LIMIT=350]-->
@@ -342,8 +343,12 @@
 
     <!-- Content description for the status bar chip shown to the user when they're sharing their screen to another app on the device [CHAR LIMIT=NONE] -->
     <string name="share_to_app_chip_accessibility_label">Sharing screen</string>
+    <!-- Content description for the status bar chip shown to the user when they're sharing their screen or audio to another app on the device [CHAR LIMIT=NONE] -->
+    <string name="share_to_app_chip_accessibility_label_generic">Sharing content</string>
     <!-- Title for a dialog shown to the user that will let them stop sharing their screen to another app on the device [CHAR LIMIT=50] -->
     <string name="share_to_app_stop_dialog_title">Stop sharing screen?</string>
+    <!-- Title for a dialog shown to the user that will let them stop sharing their screen or audio to another app on the device [CHAR LIMIT=50] -->
+    <string name="share_to_app_stop_dialog_title_generic">Stop sharing?</string>
     <!-- Text telling a user that they're currently sharing their entire screen to [host_app_name] (i.e. [host_app_name] can currently see all screen content) [CHAR LIMIT=150] -->
     <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app">You\'re currently sharing your entire screen with <xliff:g id="host_app_name" example="Screen Recorder App">%1$s</xliff:g></string>
     <!-- Text telling a user that they're currently sharing their entire screen to an app (but we don't know what app) [CHAR LIMIT=150] -->
@@ -352,6 +357,8 @@
     <string name="share_to_app_stop_dialog_message_single_app_specific">You\'re currently sharing <xliff:g id="app_being_shared_name" example="Photos App">%1$s</xliff:g></string>
     <!-- Text telling a user that they're currently sharing their screen [CHAR LIMIT=150] -->
     <string name="share_to_app_stop_dialog_message_single_app_generic">You\'re currently sharing an app</string>
+    <!-- Text telling a user that they're currently sharing something to an app [CHAR LIMIT=100] -->
+    <string name="share_to_app_stop_dialog_message_generic">You\'re currently sharing with an app</string>
     <!-- Button to stop screen sharing [CHAR LIMIT=35] -->
     <string name="share_to_app_stop_dialog_button">Stop sharing</string>
 
@@ -1311,6 +1318,10 @@
     <string name="communal_widget_picker_description">Anyone can view widgets on your lock screen, even if your tablet\'s locked.</string>
     <!-- Label for accessibility action to unselect a widget in edit mode. [CHAR LIMIT=NONE] -->
     <string name="accessibility_action_label_unselect_widget">unselect widget</string>
+    <!-- Label for accessibility action to shrink a widget in edit mode. [CHAR LIMIT=NONE] -->
+    <string name="accessibility_action_label_shrink_widget">Decrease height</string>
+    <!-- Label for accessibility action to expand a widget in edit mode. [CHAR LIMIT=NONE] -->
+    <string name="accessibility_action_label_expand_widget">Increase height</string>
     <!-- Title shown above information regarding lock screen widgets. [CHAR LIMIT=50] -->
     <string name="communal_widgets_disclaimer_title">Lock screen widgets</string>
     <!-- Information about lock screen widgets presented to the user. [CHAR LIMIT=NONE] -->
@@ -1463,6 +1474,12 @@
     <!-- The text for the notification history link. [CHAR LIMIT=40] -->
     <string name="manage_notifications_history_text">History</string>
 
+    <!-- The accessibility description for the notification settings button in the notification shade. -->
+    <string name="notification_settings_button_description">Notification settings</string>
+
+    <!-- The accessibility description for the notification history button in the notification shade. -->
+    <string name="notification_history_button_description">Notification history</string>
+
     <!-- Section title for notifications that have recently appeared. [CHAR LIMIT=40] -->
     <string name="notification_section_header_incoming">New</string>
 
@@ -3716,6 +3733,10 @@
     <!-- Title at the top of the keyboard shortcut helper UI. The helper is a component
          that shows the user which keyboard shortcuts they can use. [CHAR LIMIT=NONE] -->
     <string name="shortcut_helper_title">Keyboard shortcuts</string>
+    <!-- Title at the top of the keyboard shortcut helper UI when in customize mode. The helper
+         is a component that shows the user which keyboard shortcuts they can use.
+         [CHAR LIMIT=NONE] -->
+    <string name="shortcut_helper_customize_mode_title">Customize keyboard shortcuts</string>
     <!-- Placeholder text shown in the search box of the keyboard shortcut helper, when the user
          hasn't typed in anything in the search box yet. The helper is a  component that shows the
          user which keyboard shortcuts they can use. [CHAR LIMIT=NONE] -->
@@ -3727,6 +3748,14 @@
          use. The helper shows shortcuts in categories, which can be collapsed or expanded.
          [CHAR LIMIT=NONE] -->
     <string name="shortcut_helper_content_description_collapse_icon">Collapse icon</string>
+    <!-- Description text of the button that allows user to customize shortcuts in keyboard
+         shortcut helper The helper is a  component that shows the  user which keyboard shortcuts
+         they can use. [CHAR LIMIT=NONE] -->
+    <string name="shortcut_helper_customize_button_text">Customize</string>
+    <!-- Description text of the button that allows user to exit shortcut customization mode in
+         keyboard shortcut helper The helper is a  component that shows the  user which keyboard
+         shortcuts they can use. [CHAR LIMIT=NONE] -->
+    <string name="shortcut_helper_done_button_text">Done</string>
     <!-- Content description of the icon that allows to expand a keyboard shortcut helper category
          panel. The helper is a  component that shows the  user which keyboard shortcuts they can
          use. The helper shows shortcuts in categories, which can be collapsed or expanded.
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 1ab9242..ab45b9f 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -785,6 +785,16 @@
         <item name="android:minWidth">0dp</item>
     </style>
 
+    <style
+        name="TextAppearance.NotificationFooterButtonRedesign"
+        parent="@android:style/Widget.DeviceDefault.Button.Borderless">
+        <item name="android:textColor">?androidprv:attr/materialColorOnSurface</item>
+        <item name="android:drawableTint">?androidprv:attr/materialColorOnSurface</item>
+        <item name="android:textAllCaps">false</item>
+        <item name="android:textSize">16sp</item>
+        <item name="android:minWidth">0dp</item>
+    </style>
+
     <style name="TextAppearance.HeadsUpStatusBarText"
         parent="@*android:style/TextAppearance.DeviceDefault.Notification.Info">
     </style>
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
index a408211..8cfb4c5 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
@@ -897,6 +897,18 @@
                     break;
             }
         }
+        // A check to dismiss was made without any authentication. Verify there are no remaining SIM
+        // screens, which may happen on an unlocked lockscreen
+        if (!authenticated) {
+            SecurityMode securityMode = mSecurityModel.getSecurityMode(targetUserId);
+            if (Arrays.asList(SimPin, SimPuk).contains(securityMode)) {
+                Log.v(TAG, "Dismiss called but SIM/PUK unlock screen still required");
+                eventSubtype = -1;
+                showSecurityScreen(securityMode);
+                finish = false;
+            }
+        }
+
         // Check for device admin specified additional security measures.
         if (finish && !bypassSecondaryLockScreen) {
             Intent secondaryLockscreenIntent =
@@ -1102,8 +1114,11 @@
         }
 
         mView.initMode(mode, mGlobalSettings, mFalsingManager, mUserSwitcherController,
-                () -> showMessage(getContext().getString(R.string.keyguard_unlock_to_continue),
-                        /* colorState= */ null, /* animated= */ true), mFalsingA11yDelegate);
+                () -> {
+                        String msg = getContext().getString(R.string.keyguard_unlock_to_continue);
+                        showMessage(msg, /* colorState= */ null, /* animated= */ true);
+                        mBouncerMessageInteractor.setUnlockToContinueMessage(msg);
+                }, mFalsingA11yDelegate);
     }
 
     public void reportFailedUnlockAttempt(int userId, int timeoutMs) {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index b3ea75d..8b59370 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -80,6 +80,7 @@
 import android.hardware.fingerprint.IFingerprintAuthenticatorsRegisteredCallback;
 import android.hardware.usb.UsbManager;
 import android.nfc.NfcAdapter;
+import android.os.BatteryManager;
 import android.os.CancellationSignal;
 import android.os.Handler;
 import android.os.Looper;
@@ -345,9 +346,9 @@
     // Device provisioning state
     private boolean mDeviceProvisioned;
 
-    // Battery status
+    // Battery status (null until first update is received)
     @VisibleForTesting
-    BatteryStatus mBatteryStatus;
+    BatteryStatus mBatteryStatus = null;
     @VisibleForTesting
     boolean mIncompatibleCharger;
 
@@ -584,62 +585,68 @@
     }
 
     private void handleSimSubscriptionInfoChanged() {
-        Assert.isMainThread();
         mSimLogger.v("onSubscriptionInfoChanged()");
-        List<SubscriptionInfo> subscriptionInfos = getSubscriptionInfo(true /* forceReload */);
-        if (!subscriptionInfos.isEmpty()) {
-            for (SubscriptionInfo subInfo : subscriptionInfos) {
-                mSimLogger.logSubInfo(subInfo);
-            }
-        } else {
-            mSimLogger.v("onSubscriptionInfoChanged: list is null");
-        }
+        mBackgroundExecutor.execute(() -> {
+            final List<SubscriptionInfo> subscriptionInfos =
+                    getSubscriptionInfo(true /* forceReload */);
+            mMainExecutor.execute(() -> {
+                if (!subscriptionInfos.isEmpty()) {
+                    for (SubscriptionInfo subInfo : subscriptionInfos) {
+                        mSimLogger.logSubInfo(subInfo);
+                    }
+                } else {
+                    mSimLogger.v("onSubscriptionInfoChanged: list is null");
+                }
 
-        // Hack level over 9000: Because the subscription id is not yet valid when we see the
-        // first update in handleSimStateChange, we need to force refresh all SIM states
-        // so the subscription id for them is consistent.
-        ArrayList<SubscriptionInfo> changedSubscriptions = new ArrayList<>();
-        Set<Integer> activeSubIds = new HashSet<>();
-        for (int i = 0; i < subscriptionInfos.size(); i++) {
-            SubscriptionInfo info = subscriptionInfos.get(i);
-            activeSubIds.add(info.getSubscriptionId());
-            boolean changed = refreshSimState(info.getSubscriptionId(), info.getSimSlotIndex());
-            if (changed) {
-                changedSubscriptions.add(info);
-            }
-        }
+                // Hack level over 9000: Because the subscription id is not yet valid when we see
+                // the first update in handleSimStateChange, we need to force refresh all SIM states
+                // so the subscription id for them is consistent.
+                ArrayList<SubscriptionInfo> changedSubscriptions = new ArrayList<>();
+                Set<Integer> activeSubIds = new HashSet<>();
+                for (int i = 0; i < subscriptionInfos.size(); i++) {
+                    SubscriptionInfo info = subscriptionInfos.get(i);
+                    activeSubIds.add(info.getSubscriptionId());
+                    boolean changed = refreshSimState(info.getSubscriptionId(),
+                            info.getSimSlotIndex());
+                    if (changed) {
+                        changedSubscriptions.add(info);
+                    }
+                }
 
-        // It is possible for active subscriptions to become invalid (-1), and these will not be
-        // present in the subscriptionInfo list
-        synchronized (mSimDataLockObject) {
-            Iterator<Map.Entry<Integer, SimData>> iter = mSimDatas.entrySet().iterator();
-            while (iter.hasNext()) {
-                Map.Entry<Integer, SimData> simData = iter.next();
-                if (!activeSubIds.contains(simData.getKey())) {
-                    mSimLogger.logInvalidSubId(simData.getKey());
-                    iter.remove();
+                // It is possible for active subscriptions to become invalid (-1), and these will
+                // not be present in the subscriptionInfo list
+                synchronized (mSimDataLockObject) {
+                    Iterator<Map.Entry<Integer, SimData>> iter = mSimDatas.entrySet().iterator();
+                    while (iter.hasNext()) {
+                        Map.Entry<Integer, SimData> simData = iter.next();
+                        if (!activeSubIds.contains(simData.getKey())) {
+                            mSimLogger.logInvalidSubId(simData.getKey());
+                            iter.remove();
 
-                    SimData data = simData.getValue();
-                    for (int j = 0; j < mCallbacks.size(); j++) {
-                        KeyguardUpdateMonitorCallback cb = mCallbacks.get(j).get();
-                        if (cb != null) {
-                            cb.onSimStateChanged(data.subId, data.slotId, data.simState);
+                            SimData data = simData.getValue();
+                            for (int j = 0; j < mCallbacks.size(); j++) {
+                                KeyguardUpdateMonitorCallback cb = mCallbacks.get(j).get();
+                                if (cb != null) {
+                                    cb.onSimStateChanged(data.subId, data.slotId, data.simState);
+                                }
+                            }
                         }
                     }
-                }
-            }
 
-            for (int i = 0; i < changedSubscriptions.size(); i++) {
-                SimData data = mSimDatas.get(changedSubscriptions.get(i).getSubscriptionId());
-                for (int j = 0; j < mCallbacks.size(); j++) {
-                    KeyguardUpdateMonitorCallback cb = mCallbacks.get(j).get();
-                    if (cb != null) {
-                        cb.onSimStateChanged(data.subId, data.slotId, data.simState);
+                    for (int i = 0; i < changedSubscriptions.size(); i++) {
+                        SimData data = mSimDatas.get(
+                                changedSubscriptions.get(i).getSubscriptionId());
+                        for (int j = 0; j < mCallbacks.size(); j++) {
+                            KeyguardUpdateMonitorCallback cb = mCallbacks.get(j).get();
+                            if (cb != null) {
+                                cb.onSimStateChanged(data.subId, data.slotId, data.simState);
+                            }
+                        }
                     }
+                    callbacksRefreshCarrierInfo();
                 }
-            }
-            callbacksRefreshCarrierInfo();
-        }
+            });
+        });
     }
 
     private void handleAirplaneModeChanged() {
@@ -2367,9 +2374,27 @@
             watchForDeviceProvisioning();
         }
 
-        // Take a guess at initial SIM state, battery status and PLMN until we get an update
-        mBatteryStatus = new BatteryStatus(BATTERY_STATUS_UNKNOWN, /* level= */ -1, /* plugged= */
-                0, CHARGING_POLICY_DEFAULT, /* maxChargingWattage= */0, /* present= */true);
+        // Request the initial battery level
+        mBackgroundExecutor.execute(() -> {
+            BatteryManager batteryManager = mContext.getSystemService(BatteryManager.class);
+            int level = -1;
+            if (batteryManager != null) {
+                int capacity = batteryManager.getIntProperty(
+                        BatteryManager.BATTERY_PROPERTY_CAPACITY);
+                if (capacity >= 0 && capacity <= 100) {
+                    level = capacity;
+                }
+            }
+            // Don't override if a valid battery status update has come in
+            final BatteryStatus status = new BatteryStatus(BATTERY_STATUS_UNKNOWN,
+                    /* level= */ level, /* plugged= */ 0, CHARGING_POLICY_DEFAULT,
+                    /* maxChargingWattage= */0, /* present= */true);
+            mMainExecutor.execute(() -> {
+                if (mBatteryStatus == null) {
+                    handleBatteryUpdate(status);
+                }
+            });
+        });
 
         // Watch for interesting updates
         final IntentFilter filter = new IntentFilter();
@@ -2504,6 +2529,11 @@
         if (mUserTracker.isUserSwitching()) {
             handleUserSwitching(mUserTracker.getUserId(), () -> {});
         }
+
+        // Force the cache to be initialized
+        mBackgroundExecutor.execute(() -> {
+            getSubscriptionInfo(/* forceReload= */ true);
+        });
     }
 
     @VisibleForTesting
@@ -3591,6 +3621,9 @@
     }
 
     private boolean isBatteryUpdateInteresting(BatteryStatus old, BatteryStatus current) {
+        if (old == null) {
+            return true;
+        }
         final boolean nowPluggedIn = current.isPluggedIn();
         final boolean wasPluggedIn = old.isPluggedIn();
         final boolean stateChangedWhilePluggedIn = wasPluggedIn && nowPluggedIn
@@ -3687,7 +3720,9 @@
 
     private void sendUpdates(KeyguardUpdateMonitorCallback callback) {
         // Notify listener of the current state
-        callback.onRefreshBatteryInfo(mBatteryStatus);
+        if (mBatteryStatus != null) {
+            callback.onRefreshBatteryInfo(mBatteryStatus);
+        }
         callback.onTimeChanged();
         callback.onPhoneStateChanged(mPhoneState);
         callback.onRefreshCarrierInfo();
@@ -3827,11 +3862,14 @@
      * @see #isSimPinSecure(int)
      */
     public boolean isSimPinSecure() {
-        // True if any SIM is pin secure
-        for (SubscriptionInfo info : getSubscriptionInfo(false /* forceReload */)) {
-            if (isSimPinSecure(getSimState(info.getSubscriptionId()))) return true;
+        synchronized (mSimDataLockObject) {
+            for (SimData data : mSimDatas.values()) {
+                if (isSimPinSecure(data.simState)) {
+                    return true;
+                }
+            }
+            return false;
         }
-        return false;
     }
 
     public int getSimState(int subId) {
diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
index 9b5d5b6..46e45aa 100644
--- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
+++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
@@ -92,7 +92,6 @@
 import com.android.systemui.statusbar.events.PrivacyDotViewController;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.util.concurrency.DelayableExecutor;
-import com.android.systemui.util.concurrency.ThreadFactory;
 import com.android.systemui.util.kotlin.JavaAdapter;
 import com.android.systemui.util.settings.SecureSettings;
 
@@ -147,7 +146,6 @@
     private CameraAvailabilityListener mCameraListener;
     private final UserTracker mUserTracker;
     private final PrivacyDotViewController mDotViewController;
-    private final ThreadFactory mThreadFactory;
     private final DecorProviderFactory mDotFactory;
     private final FaceScanningProviderFactory mFaceScanningFactory;
     private final CameraProtectionLoader mCameraProtectionLoader;
@@ -172,7 +170,6 @@
     private ViewCaptureAwareWindowManager mWindowManager;
     private int mRotation;
     private UserSettingObserver mColorInversionSetting;
-    @Nullable
     private DelayableExecutor mExecutor;
     private Handler mHandler;
     boolean mPendingConfigChange;
@@ -327,27 +324,28 @@
     }
 
     @Inject
-    public ScreenDecorations(Context context,
+    public ScreenDecorations(
+            Context context,
             SecureSettings secureSettings,
             CommandRegistry commandRegistry,
             UserTracker userTracker,
             DisplayTracker displayTracker,
             PrivacyDotViewController dotViewController,
-            ThreadFactory threadFactory,
             PrivacyDotDecorProviderFactory dotFactory,
             FaceScanningProviderFactory faceScanningFactory,
             ScreenDecorationsLogger logger,
             FacePropertyRepository facePropertyRepository,
             JavaAdapter javaAdapter,
             CameraProtectionLoader cameraProtectionLoader,
-            ViewCaptureAwareWindowManager viewCaptureAwareWindowManager) {
+            ViewCaptureAwareWindowManager viewCaptureAwareWindowManager,
+            @ScreenDecorationsThread Handler handler,
+            @ScreenDecorationsThread DelayableExecutor executor) {
         mContext = context;
         mSecureSettings = secureSettings;
         mCommandRegistry = commandRegistry;
         mUserTracker = userTracker;
         mDisplayTracker = displayTracker;
         mDotViewController = dotViewController;
-        mThreadFactory = threadFactory;
         mDotFactory = dotFactory;
         mFaceScanningFactory = faceScanningFactory;
         mCameraProtectionLoader = cameraProtectionLoader;
@@ -356,6 +354,8 @@
         mFacePropertyRepository = facePropertyRepository;
         mJavaAdapter = javaAdapter;
         mWindowManager = viewCaptureAwareWindowManager;
+        mHandler = handler;
+        mExecutor = executor;
     }
 
     private final ScreenDecorCommand.Callback mScreenDecorCommandCallback = (cmd, pw) -> {
@@ -403,10 +403,7 @@
             Log.i(TAG, "ScreenDecorations is disabled");
             return;
         }
-        mHandler = mThreadFactory.buildHandlerOnNewThread("ScreenDecorations");
-        mExecutor = mThreadFactory.buildDelayableExecutorOnHandler(mHandler);
         mExecutor.execute(this::startOnScreenDecorationsThread);
-        mDotViewController.setUiExecutor(mExecutor);
         mCommandRegistry.registerCommand(ScreenDecorCommand.SCREEN_DECOR_CMD_NAME,
                 () -> new ScreenDecorCommand(mScreenDecorCommandCallback));
     }
diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorationsModule.kt b/packages/SystemUI/src/com/android/systemui/ScreenDecorationsModule.kt
index 6fc50fb..6786a71 100644
--- a/packages/SystemUI/src/com/android/systemui/ScreenDecorationsModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorationsModule.kt
@@ -17,16 +17,23 @@
 package com.android.systemui
 
 import android.content.Context
+import android.os.Handler
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.decor.FaceScanningProviderFactory
 import com.android.systemui.decor.FaceScanningProviderFactoryImpl
 import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener
+import com.android.systemui.util.concurrency.DelayableExecutor
+import com.android.systemui.util.concurrency.ThreadFactory
 import dagger.Binds
 import dagger.Module
 import dagger.Provides
 import dagger.multibindings.ClassKey
 import dagger.multibindings.IntoMap
 import dagger.multibindings.IntoSet
+import java.util.concurrent.Executor
+import javax.inject.Qualifier
+
+@Qualifier annotation class ScreenDecorationsThread
 
 @Module
 interface ScreenDecorationsModule {
@@ -41,6 +48,12 @@
     @IntoSet
     fun bindScreenDecorationsConfigListener(impl: ScreenDecorations): ConfigurationListener
 
+    @Binds
+    @ScreenDecorationsThread
+    fun screenDecorationsExecutor(
+        @ScreenDecorationsThread delayableExecutor: DelayableExecutor
+    ): Executor
+
     companion object {
         @Provides
         @SysUISingleton
@@ -50,5 +63,22 @@
         ): FaceScanningProviderFactory {
             return creator.create(context)
         }
+
+        @Provides
+        @SysUISingleton
+        @ScreenDecorationsThread
+        fun screenDecorationsHandler(threadFactory: ThreadFactory): Handler {
+            return threadFactory.buildHandlerOnNewThread("ScreenDecorations")
+        }
+
+        @Provides
+        @SysUISingleton
+        @ScreenDecorationsThread
+        fun screenDecorationsDelayableExecutor(
+            @ScreenDecorationsThread handler: Handler,
+            threadFactory: ThreadFactory,
+        ): DelayableExecutor {
+            return threadFactory.buildDelayableExecutorOnHandler(handler)
+        }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/ambient/touch/BouncerSwipeTouchHandler.kt b/packages/SystemUI/src/com/android/systemui/ambient/touch/BouncerSwipeTouchHandler.kt
index 7b3d337..e365b77 100644
--- a/packages/SystemUI/src/com/android/systemui/ambient/touch/BouncerSwipeTouchHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/ambient/touch/BouncerSwipeTouchHandler.kt
@@ -27,6 +27,7 @@
 import android.view.MotionEvent
 import android.view.VelocityTracker
 import androidx.annotation.VisibleForTesting
+import com.android.app.tracing.coroutines.launchTraced as launch
 import com.android.internal.logging.UiEvent
 import com.android.internal.logging.UiEventLogger
 import com.android.systemui.Flags
@@ -54,7 +55,6 @@
 import kotlin.math.max
 import kotlin.math.min
 import kotlinx.coroutines.CoroutineScope
-import com.android.app.tracing.coroutines.launchTraced as launch
 
 /** Monitor for tracking touches on the DreamOverlay to bring up the bouncer. */
 class BouncerSwipeTouchHandler
diff --git a/packages/SystemUI/src/com/android/systemui/ambient/touch/ShadeTouchHandler.kt b/packages/SystemUI/src/com/android/systemui/ambient/touch/ShadeTouchHandler.kt
index 05a1009..50e62a8 100644
--- a/packages/SystemUI/src/com/android/systemui/ambient/touch/ShadeTouchHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/ambient/touch/ShadeTouchHandler.kt
@@ -22,6 +22,7 @@
 import android.view.InputEvent
 import android.view.MotionEvent
 import androidx.annotation.VisibleForTesting
+import com.android.app.tracing.coroutines.launchTraced as launch
 import com.android.systemui.Flags
 import com.android.systemui.ambient.touch.TouchHandler.TouchSession
 import com.android.systemui.ambient.touch.dagger.ShadeModule
@@ -38,7 +39,6 @@
 import javax.inject.Provider
 import kotlin.math.abs
 import kotlinx.coroutines.CoroutineScope
-import com.android.app.tracing.coroutines.launchTraced as launch
 
 /**
  * [ShadeTouchHandler] is responsible for handling swipe down gestures over dream to bring down the
diff --git a/packages/SystemUI/src/com/android/systemui/authentication/shared/model/BouncerInputSide.kt b/packages/SystemUI/src/com/android/systemui/authentication/shared/model/BouncerInputSide.kt
new file mode 100644
index 0000000..e52898d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/authentication/shared/model/BouncerInputSide.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.authentication.shared.model
+
+import android.provider.Settings.Global.ONE_HANDED_KEYGUARD_SIDE_LEFT
+import android.provider.Settings.Global.ONE_HANDED_KEYGUARD_SIDE_RIGHT
+import com.android.systemui.authentication.shared.model.BouncerInputSide.LEFT
+import com.android.systemui.authentication.shared.model.BouncerInputSide.RIGHT
+
+/** Denotes which side of the bouncer the input area appears, applicable to large screen devices. */
+enum class BouncerInputSide(val settingValue: Int) {
+    LEFT(ONE_HANDED_KEYGUARD_SIDE_LEFT),
+    RIGHT(ONE_HANDED_KEYGUARD_SIDE_RIGHT),
+}
+
+/** Map the setting value to [BouncerInputSide] enum. */
+fun Int.toBouncerInputSide(): BouncerInputSide? {
+    return when (this) {
+        ONE_HANDED_KEYGUARD_SIDE_LEFT -> LEFT
+        ONE_HANDED_KEYGUARD_SIDE_RIGHT -> RIGHT
+        else -> null
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/BouncerRepository.kt b/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/BouncerRepository.kt
index 94e0854..f424de9 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/BouncerRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/BouncerRepository.kt
@@ -16,19 +16,66 @@
 
 package com.android.systemui.bouncer.data.repository
 
+import android.content.Context
+import android.provider.Settings.Global.ONE_HANDED_KEYGUARD_SIDE
+import com.android.systemui.authentication.shared.model.BouncerInputSide
+import com.android.systemui.authentication.shared.model.toBouncerInputSide
 import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.flags.FeatureFlagsClassic
 import com.android.systemui.flags.Flags
+import com.android.systemui.res.R
+import com.android.systemui.util.settings.GlobalSettings
 import javax.inject.Inject
+import kotlinx.coroutines.flow.MutableStateFlow
 
 /** Provides access to bouncer-related application state. */
 @SysUISingleton
 class BouncerRepository
 @Inject
 constructor(
+    @Application private val applicationContext: Context,
+    private val globalSettings: GlobalSettings,
     private val flags: FeatureFlagsClassic,
 ) {
+    val scale: MutableStateFlow<Float> = MutableStateFlow(1.0f)
+
     /** Whether the user switcher should be displayed within the bouncer UI on large screens. */
-    val isUserSwitcherVisible: Boolean
-        get() = flags.isEnabled(Flags.FULL_SCREEN_USER_SWITCHER)
+    val isUserSwitcherEnabledInConfig: Boolean
+        get() =
+            applicationContext.resources.getBoolean(R.bool.config_enableBouncerUserSwitcher) &&
+                flags.isEnabled(Flags.FULL_SCREEN_USER_SWITCHER)
+
+    /** Whether the one handed bouncer is supported for this device. */
+    val isOneHandedBouncerSupportedInConfig: Boolean
+        get() = applicationContext.resources.getBoolean(R.bool.can_use_one_handed_bouncer)
+
+    /**
+     * Preferred side of the screen where the input area on the bouncer should be. This is
+     * applicable for large screen devices (foldables and tablets).
+     */
+    val preferredBouncerInputSide: MutableStateFlow<BouncerInputSide?> =
+        MutableStateFlow(getPreferredInputSideSetting())
+
+    /** X coordinate of the last recorded touch position on the lockscreen. */
+    val lastRecordedLockscreenTouchPosition = MutableStateFlow<Float?>(null)
+
+    /** Save the preferred bouncer input side. */
+    fun setPreferredBouncerInputSide(inputSide: BouncerInputSide) {
+        globalSettings.putInt(ONE_HANDED_KEYGUARD_SIDE, inputSide.settingValue)
+        // used to only trigger another emission on the flow.
+        preferredBouncerInputSide.value = inputSide
+    }
+
+    /**
+     * Record the x coordinate of the last touch position on the lockscreen. This will be used to
+     * determine which side of the bouncer the input area should be shown.
+     */
+    fun recordLockscreenTouchPosition(x: Float) {
+        lastRecordedLockscreenTouchPosition.value = x
+    }
+
+    fun getPreferredInputSideSetting(): BouncerInputSide? {
+        return globalSettings.getInt(ONE_HANDED_KEYGUARD_SIDE, -1).toBouncerInputSide()
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerActionButtonInteractor.kt b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerActionButtonInteractor.kt
index da01c58..b95ce69 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerActionButtonInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerActionButtonInteractor.kt
@@ -30,7 +30,6 @@
 import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor
 import com.android.systemui.bouncer.data.repository.EmergencyServicesRepository
 import com.android.systemui.bouncer.shared.model.BouncerActionButtonModel
-import com.android.systemui.bouncer.ui.helper.BouncerHapticPlayer
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.Background
@@ -75,7 +74,6 @@
     private val metricsLogger: MetricsLogger,
     private val dozeLogger: DozeLogger,
     private val sceneInteractor: Lazy<SceneInteractor>,
-    private val bouncerHapticPlayer: BouncerHapticPlayer,
 ) {
     /** The bouncer action button. If `null`, the button should not be shown. */
     val actionButton: Flow<BouncerActionButtonModel?> =
@@ -90,43 +88,38 @@
                 )
                 .map {
                     when {
-                        isReturnToCallButton() -> returnToCallButtonModel
-                        isEmergencyCallButton() -> emergencyCallButtonModel
+                        isReturnToCallButton() ->
+                            BouncerActionButtonModel.ReturnToCallButtonModel(
+                                labelResourceId = R.string.lockscreen_return_to_call
+                            )
+                        isEmergencyCallButton() ->
+                            BouncerActionButtonModel.EmergencyButtonModel(
+                                labelResourceId = R.string.lockscreen_emergency_call
+                            )
                         else -> null // Do not show the button.
                     }
                 }
                 .distinctUntilChanged()
         }
 
-    private val returnToCallButtonModel: BouncerActionButtonModel by lazy {
-        BouncerActionButtonModel(
-            label = applicationContext.getString(R.string.lockscreen_return_to_call),
-            onClick = {
-                prepareToPerformAction()
-                returnToCall()
-            },
-            onLongClick = null,
-        )
+    fun onReturnToCallButtonClicked() {
+        prepareToPerformAction()
+        returnToCall()
     }
 
-    private val emergencyCallButtonModel: BouncerActionButtonModel by lazy {
-        BouncerActionButtonModel(
-            label = applicationContext.getString(R.string.lockscreen_emergency_call),
-            onClick = {
-                // TODO(b/373930432): haptics should be played at the UI layer -> refactor
-                bouncerHapticPlayer.playEmergencyButtonClickFeedback()
-                prepareToPerformAction()
-                dozeLogger.logEmergencyCall()
-                startEmergencyDialerActivity()
-            },
-            // TODO(b/369767936): The long click detector doesn't work properly, investigate.
-            onLongClick = {
-                if (emergencyAffordanceManager.needsEmergencyAffordance()) {
-                    prepareToPerformAction()
-                    emergencyAffordanceManager.performEmergencyCall()
-                }
-            },
-        )
+    fun onEmergencyButtonClicked() {
+        prepareToPerformAction()
+        dozeLogger.logEmergencyCall()
+        startEmergencyDialerActivity()
+    }
+
+    fun onEmergencyButtonLongClicked() {
+        if (emergencyAffordanceManager.needsEmergencyAffordance()) {
+            prepareToPerformAction()
+
+            // TODO(b/369767936): Check that !longPressWasDragged before invoking.
+            emergencyAffordanceManager.performEmergencyCall()
+        }
     }
 
     private fun startEmergencyDialerActivity() {
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt
index 92fcf39..e178c09 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt
@@ -25,16 +25,19 @@
 import com.android.systemui.authentication.shared.model.AuthenticationMethodModel.Pattern
 import com.android.systemui.authentication.shared.model.AuthenticationMethodModel.Pin
 import com.android.systemui.authentication.shared.model.AuthenticationMethodModel.Sim
+import com.android.systemui.authentication.shared.model.BouncerInputSide
 import com.android.systemui.bouncer.data.repository.BouncerRepository
 import com.android.systemui.bouncer.shared.logging.BouncerUiEvent
 import com.android.systemui.classifier.FalsingClassifier
 import com.android.systemui.classifier.domain.interactor.FalsingInteractor
+import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor
 import com.android.systemui.log.SessionTracker
 import com.android.systemui.power.domain.interactor.PowerInteractor
 import com.android.systemui.scene.domain.interactor.SceneBackInteractor
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
 import com.android.systemui.scene.shared.model.Scenes
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
@@ -43,6 +46,8 @@
 import kotlinx.coroutines.flow.MutableSharedFlow
 import kotlinx.coroutines.flow.SharedFlow
 import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.filter
 import kotlinx.coroutines.flow.map
 
@@ -60,6 +65,7 @@
     private val uiEventLogger: UiEventLogger,
     private val sessionTracker: SessionTracker,
     sceneBackInteractor: SceneBackInteractor,
+    private val configurationInteractor: ConfigurationInteractor,
 ) {
     private val _onIncorrectBouncerInput = MutableSharedFlow<Unit>()
     val onIncorrectBouncerInput: SharedFlow<Unit> = _onIncorrectBouncerInput
@@ -78,8 +84,47 @@
         authenticationInteractor.isPinEnhancedPrivacyEnabled
 
     /** Whether the user switcher should be displayed within the bouncer UI on large screens. */
-    val isUserSwitcherVisible: Boolean
-        get() = repository.isUserSwitcherVisible
+    val isUserSwitcherVisible: Flow<Boolean> =
+        authenticationInteractor.authenticationMethod.map { authMethod ->
+            when (authMethod) {
+                Sim -> false
+                else -> repository.isUserSwitcherEnabledInConfig
+            }
+        }
+
+    /**
+     * Whether one handed bouncer mode is supported on large screen devices. This allows user to
+     * double tap on the half of the screen to bring the bouncer input to that side of the screen.
+     */
+    val isOneHandedModeSupported: Flow<Boolean> =
+        combine(
+            isUserSwitcherVisible,
+            authenticationInteractor.authenticationMethod,
+            configurationInteractor.onAnyConfigurationChange,
+        ) { userSwitcherVisible, authMethod, _ ->
+            userSwitcherVisible ||
+                (repository.isOneHandedBouncerSupportedInConfig && (authMethod !is Password))
+        }
+
+    /**
+     * Preferred side of the screen where the input area on the bouncer should be. This is
+     * applicable for large screen devices (foldables and tablets).
+     */
+    val preferredBouncerInputSide: Flow<BouncerInputSide?> =
+        combine(
+            configurationInteractor.onAnyConfigurationChange,
+            repository.preferredBouncerInputSide,
+        ) { _, _ ->
+            // always read the setting as that can change outside of this
+            // repository (tests/manual testing)
+            val preferredInputSide = repository.getPreferredInputSideSetting()
+            when {
+                preferredInputSide != null -> preferredInputSide
+                repository.isUserSwitcherEnabledInConfig -> BouncerInputSide.RIGHT
+                repository.isOneHandedBouncerSupportedInConfig -> BouncerInputSide.LEFT
+                else -> null
+            }
+        }
 
     private val _onImeHiddenByUser = MutableSharedFlow<Unit>()
     /** Emits a [Unit] each time the IME (keyboard) is hidden by the user. */
@@ -93,6 +138,12 @@
             }
             .map {}
 
+    /** X coordinate of the last recorded touch position on the lockscreen. */
+    val lastRecordedLockscreenTouchPosition = repository.lastRecordedLockscreenTouchPosition
+
+    /** Value between 0-1 that specifies by how much the bouncer UI should be scaled down. */
+    val scale: StateFlow<Float> = repository.scale.asStateFlow()
+
     /** The scene to show when bouncer is dismissed. */
     val dismissDestination: Flow<SceneKey> =
         sceneBackInteractor.backScene
@@ -129,6 +180,37 @@
         )
     }
 
+    /** Update the preferred input side for the bouncer. */
+    fun setPreferredBouncerInputSide(inputSide: BouncerInputSide) {
+        repository.setPreferredBouncerInputSide(inputSide)
+    }
+
+    /**
+     * Record the x coordinate of the last touch position on the lockscreen. This will be used to
+     * determine which side of the bouncer the input area should be shown.
+     */
+    fun recordKeyguardTouchPosition(x: Float) {
+        // todo (b/375245685) investigate why this is not working as expected when it is
+        //  wired up with SBKVM
+        repository.recordLockscreenTouchPosition(x)
+    }
+
+    fun onBackEventProgressed(progress: Float) {
+        // this is applicable only for compose bouncer without flexiglass
+        SceneContainerFlag.assertInLegacyMode()
+        repository.scale.value = (mapBackEventProgressToScale(progress))
+    }
+
+    fun onBackEventCancelled() {
+        // this is applicable only for compose bouncer without flexiglass
+        SceneContainerFlag.assertInLegacyMode()
+        repository.scale.value = DEFAULT_SCALE
+    }
+
+    fun resetScale() {
+        repository.scale.value = DEFAULT_SCALE
+    }
+
     /**
      * Attempts to authenticate based on the given user input.
      *
@@ -180,7 +262,7 @@
             } else if (authResult == AuthenticationResult.FAILED) {
                 uiEventLogger.log(
                     BouncerUiEvent.BOUNCER_PASSWORD_FAILURE,
-                    sessionTracker.getSessionId(SESSION_KEYGUARD)
+                    sessionTracker.getSessionId(SESSION_KEYGUARD),
                 )
             }
         }
@@ -192,4 +274,15 @@
     suspend fun onImeHiddenByUser() {
         _onImeHiddenByUser.emit(Unit)
     }
+
+    private fun mapBackEventProgressToScale(progress: Float): Float {
+        // TODO(b/263819310): Update the interpolator to match spec.
+        return MIN_BACK_SCALE + (1 - MIN_BACK_SCALE) * (1 - progress)
+    }
+
+    companion object {
+        // How much the view scales down to during back gestures.
+        private const val MIN_BACK_SCALE: Float = 0.9f
+        private const val DEFAULT_SCALE: Float = 1.0f
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractor.kt b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractor.kt
index d125c36..e52ddb2 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractor.kt
@@ -81,7 +81,7 @@
         deviceEntryBiometricsAllowedInteractor.isFingerprintCurrentlyAllowedOnBouncer.stateIn(
             applicationScope,
             SharingStarted.Eagerly,
-            false
+            false,
         )
 
     private val currentSecurityMode
@@ -114,13 +114,13 @@
                         BiometricSourceType.FACE ->
                             BouncerMessageStrings.incorrectFaceInput(
                                     currentSecurityMode.toAuthModel(),
-                                    isFingerprintAuthCurrentlyAllowedOnBouncer.value
+                                    isFingerprintAuthCurrentlyAllowedOnBouncer.value,
                                 )
                                 .toMessage()
                         else ->
                             BouncerMessageStrings.defaultMessage(
                                     currentSecurityMode.toAuthModel(),
-                                    isFingerprintAuthCurrentlyAllowedOnBouncer.value
+                                    isFingerprintAuthCurrentlyAllowedOnBouncer.value,
                                 )
                                 .toMessage()
                     },
@@ -130,7 +130,7 @@
 
             override fun onBiometricAcquired(
                 biometricSourceType: BiometricSourceType?,
-                acquireInfo: Int
+                acquireInfo: Int,
             ) {
                 if (
                     repository.getMessageSource() == BiometricSourceType.FACE &&
@@ -143,7 +143,7 @@
             override fun onBiometricAuthenticated(
                 userId: Int,
                 biometricSourceType: BiometricSourceType?,
-                isStrongBiometric: Boolean
+                isStrongBiometric: Boolean,
             ) {
                 repository.setMessage(defaultMessage, biometricSourceType)
             }
@@ -169,7 +169,7 @@
                 deviceEntryBiometricsAllowedInteractor.isFingerprintLockedOut,
                 deviceEntryBiometricsAllowedInteractor.isFaceLockedOut,
                 isFingerprintAuthCurrentlyAllowedOnBouncer,
-                ::Septuple
+                ::Septuple,
             )
             .map { (_, flags, _, biometricsEnrolledAndEnabled, fpLockedOut, faceLockedOut, _) ->
                 val isTrustUsuallyManaged = trustRepository.isCurrentUserTrustUsuallyManaged.value
@@ -220,14 +220,14 @@
                     } else {
                         BouncerMessageStrings.faceLockedOut(
                                 currentSecurityMode.toAuthModel(),
-                                isFingerprintAuthCurrentlyAllowedOnBouncer.value
+                                isFingerprintAuthCurrentlyAllowedOnBouncer.value,
                             )
                             .toMessage()
                     }
                 } else if (flags.isSomeAuthRequiredAfterAdaptiveAuthRequest) {
                     BouncerMessageStrings.authRequiredAfterAdaptiveAuthRequest(
                             currentSecurityMode.toAuthModel(),
-                            isFingerprintAuthCurrentlyAllowedOnBouncer.value
+                            isFingerprintAuthCurrentlyAllowedOnBouncer.value,
                         )
                         .toMessage()
                 } else if (
@@ -236,19 +236,19 @@
                 ) {
                     BouncerMessageStrings.nonStrongAuthTimeout(
                             currentSecurityMode.toAuthModel(),
-                            isFingerprintAuthCurrentlyAllowedOnBouncer.value
+                            isFingerprintAuthCurrentlyAllowedOnBouncer.value,
                         )
                         .toMessage()
                 } else if (isTrustUsuallyManaged && flags.someAuthRequiredAfterUserRequest) {
                     BouncerMessageStrings.trustAgentDisabled(
                             currentSecurityMode.toAuthModel(),
-                            isFingerprintAuthCurrentlyAllowedOnBouncer.value
+                            isFingerprintAuthCurrentlyAllowedOnBouncer.value,
                         )
                         .toMessage()
                 } else if (isTrustUsuallyManaged && flags.someAuthRequiredAfterTrustAgentExpired) {
                     BouncerMessageStrings.trustAgentDisabled(
                             currentSecurityMode.toAuthModel(),
-                            isFingerprintAuthCurrentlyAllowedOnBouncer.value
+                            isFingerprintAuthCurrentlyAllowedOnBouncer.value,
                         )
                         .toMessage()
                 } else if (trustOrBiometricsAvailable && flags.isInUserLockdown) {
@@ -292,7 +292,7 @@
         repository.setMessage(
             BouncerMessageStrings.incorrectSecurityInput(
                     currentSecurityMode.toAuthModel(),
-                    isFingerprintAuthCurrentlyAllowedOnBouncer.value
+                    isFingerprintAuthCurrentlyAllowedOnBouncer.value,
                 )
                 .toMessage()
         )
@@ -304,19 +304,30 @@
             defaultMessage(
                 currentSecurityMode,
                 value,
-                isFingerprintAuthCurrentlyAllowedOnBouncer.value
+                isFingerprintAuthCurrentlyAllowedOnBouncer.value,
             ),
             BiometricSourceType.FINGERPRINT,
         )
     }
 
+    fun setUnlockToContinueMessage(value: String) {
+        if (!Flags.revampedBouncerMessages()) return
+        repository.setMessage(
+            defaultMessage(
+                currentSecurityMode,
+                value,
+                isFingerprintAuthCurrentlyAllowedOnBouncer.value,
+            )
+        )
+    }
+
     fun setFaceAcquisitionMessage(value: String?) {
         if (!Flags.revampedBouncerMessages()) return
         repository.setMessage(
             defaultMessage(
                 currentSecurityMode,
                 value,
-                isFingerprintAuthCurrentlyAllowedOnBouncer.value
+                isFingerprintAuthCurrentlyAllowedOnBouncer.value,
             ),
             BiometricSourceType.FACE,
         )
@@ -329,7 +340,7 @@
             defaultMessage(
                 currentSecurityMode,
                 value,
-                isFingerprintAuthCurrentlyAllowedOnBouncer.value
+                isFingerprintAuthCurrentlyAllowedOnBouncer.value,
             )
         )
     }
@@ -338,7 +349,7 @@
         get() =
             BouncerMessageStrings.defaultMessage(
                     currentSecurityMode.toAuthModel(),
-                    isFingerprintAuthCurrentlyAllowedOnBouncer.value
+                    isFingerprintAuthCurrentlyAllowedOnBouncer.value,
                 )
                 .toMessage()
 
@@ -400,7 +411,7 @@
 private fun defaultMessage(
     securityMode: SecurityMode,
     secondaryMessage: String?,
-    fpAuthIsAllowed: Boolean
+    fpAuthIsAllowed: Boolean,
 ): BouncerMessageModel {
     return BouncerMessageModel(
         message =
@@ -408,21 +419,21 @@
                 messageResId =
                     BouncerMessageStrings.defaultMessage(
                             securityMode.toAuthModel(),
-                            fpAuthIsAllowed
+                            fpAuthIsAllowed,
                         )
                         .toMessage()
                         .message
                         ?.messageResId,
-                animate = false
+                animate = false,
             ),
-        secondaryMessage = Message(message = secondaryMessage, animate = false)
+        secondaryMessage = Message(message = secondaryMessage, animate = false),
     )
 }
 
 private fun Pair<Int, Int>.toMessage(): BouncerMessageModel {
     return BouncerMessageModel(
         message = Message(messageResId = this.first, animate = false),
-        secondaryMessage = Message(messageResId = this.second, animate = false)
+        secondaryMessage = Message(messageResId = this.second, animate = false),
     )
 }
 
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/shared/model/BouncerActionButtonModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/shared/model/BouncerActionButtonModel.kt
index 7f1730c..69f37e1 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/shared/model/BouncerActionButtonModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/shared/model/BouncerActionButtonModel.kt
@@ -16,17 +16,16 @@
 
 package com.android.systemui.bouncer.shared.model
 
+import androidx.annotation.StringRes
+
 /** Models the action button on the bouncer. */
-data class BouncerActionButtonModel(
-    /** The text to be shown on the button. */
-    val label: String,
+sealed class BouncerActionButtonModel(
+    /** The resource Id of the text to be shown on the button. */
+    @StringRes val labelResId: Int
+) {
+    data class EmergencyButtonModel(@StringRes private val labelResourceId: Int) :
+        BouncerActionButtonModel(labelResourceId)
 
-    /** The action to perform when the user clicks on the button. */
-    val onClick: () -> Unit,
-
-    /**
-     * The action to perform when the user long-clicks on the button. When not provided, long-clicks
-     * will be treated as regular clicks.
-     */
-    val onLongClick: (() -> Unit)? = null,
-)
+    data class ReturnToCallButtonModel(@StringRes private val labelResourceId: Int) :
+        BouncerActionButtonModel(labelResourceId)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/helper/BouncerSceneLayout.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/helper/BouncerSceneLayout.kt
index 7f97718..554dd69 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/helper/BouncerSceneLayout.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/helper/BouncerSceneLayout.kt
@@ -45,7 +45,7 @@
 fun calculateLayoutInternal(
     width: SizeClass,
     height: SizeClass,
-    isSideBySideSupported: Boolean,
+    isOneHandedModeSupported: Boolean,
 ): BouncerSceneLayout {
     return when (height) {
         SizeClass.COMPACT -> BouncerSceneLayout.SPLIT_BOUNCER
@@ -61,6 +61,6 @@
                 SizeClass.MEDIUM -> BouncerSceneLayout.BELOW_USER_SWITCHER
                 SizeClass.EXPANDED -> BouncerSceneLayout.BESIDE_USER_SWITCHER
             }
-    }.takeIf { it != BouncerSceneLayout.BESIDE_USER_SWITCHER || isSideBySideSupported }
+    }.takeIf { it != BouncerSceneLayout.BESIDE_USER_SWITCHER || isOneHandedModeSupported }
         ?: BouncerSceneLayout.STANDARD_BOUNCER
 }
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerSceneContentViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerSceneContentViewModel.kt
index 0d7be8c..47d91374 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerSceneContentViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerSceneContentViewModel.kt
@@ -27,6 +27,7 @@
 import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor
 import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
 import com.android.systemui.authentication.shared.model.AuthenticationWipeModel
+import com.android.systemui.authentication.shared.model.BouncerInputSide
 import com.android.systemui.bouncer.domain.interactor.BouncerActionButtonInteractor
 import com.android.systemui.bouncer.domain.interactor.BouncerInteractor
 import com.android.systemui.bouncer.shared.model.BouncerActionButtonModel
@@ -65,6 +66,7 @@
     private val passwordViewModelFactory: PasswordBouncerViewModel.Factory,
     private val bouncerHapticPlayer: BouncerHapticPlayer,
     private val keyguardMediaKeyInteractor: KeyguardMediaKeyInteractor,
+    private val bouncerActionButtonInteractor: BouncerActionButtonInteractor,
 ) : ExclusiveActivatable() {
     private val _selectedUserImage = MutableStateFlow<Bitmap?>(null)
     val selectedUserImage: StateFlow<Bitmap?> = _selectedUserImage.asStateFlow()
@@ -76,8 +78,8 @@
     val userSwitcherDropdown: StateFlow<List<UserSwitcherDropdownItemViewModel>> =
         _userSwitcherDropdown.asStateFlow()
 
-    val isUserSwitcherVisible: Boolean
-        get() = bouncerInteractor.isUserSwitcherVisible
+    private val _isUserSwitcherVisible = MutableStateFlow(false)
+    val isUserSwitcherVisible: StateFlow<Boolean> = _isUserSwitcherVisible.asStateFlow()
 
     /** View-model for the current UI, based on the current authentication method. */
     private val _authMethodViewModel = MutableStateFlow<AuthMethodBouncerViewModel?>(null)
@@ -117,17 +119,19 @@
      */
     val actionButton: StateFlow<BouncerActionButtonModel?> = _actionButton.asStateFlow()
 
-    private val _isSideBySideSupported =
-        MutableStateFlow(isSideBySideSupported(authMethodViewModel.value))
+    private val _isOneHandedModeSupported = MutableStateFlow(false)
     /**
-     * Whether the "side-by-side" layout is supported.
+     * Whether the one-handed mode is supported.
      *
      * When presented on its own, without a user switcher (e.g. not on communal devices like
      * tablets, for example), some authentication method UIs don't do well if they're shown in the
      * side-by-side layout; these need to be shown with the standard layout so they can take up as
      * much width as possible.
      */
-    val isSideBySideSupported: StateFlow<Boolean> = _isSideBySideSupported.asStateFlow()
+    val isOneHandedModeSupported: StateFlow<Boolean> = _isOneHandedModeSupported.asStateFlow()
+
+    private val _isInputPreferredOnLeftSide = MutableStateFlow(false)
+    val isInputPreferredOnLeftSide = _isInputPreferredOnLeftSide.asStateFlow()
 
     private val _isFoldSplitRequired =
         MutableStateFlow(isFoldSplitRequired(authMethodViewModel.value))
@@ -137,11 +141,15 @@
      */
     val isFoldSplitRequired: StateFlow<Boolean> = _isFoldSplitRequired.asStateFlow()
 
+    /** How much the bouncer UI should be scaled. */
+    val scale: StateFlow<Float> = bouncerInteractor.scale
+
     private val _isInputEnabled =
         MutableStateFlow(authenticationInteractor.lockoutEndTimestamp == null)
     private val isInputEnabled: StateFlow<Boolean> = _isInputEnabled.asStateFlow()
 
     override suspend fun onActivated(): Nothing {
+        bouncerInteractor.resetScale()
         coroutineScope {
             launch { message.activate() }
             launch {
@@ -199,9 +207,41 @@
             launch { actionButtonInteractor.actionButton.collect { _actionButton.value = it } }
 
             launch {
-                authMethodViewModel
-                    .map { authMethod -> isSideBySideSupported(authMethod) }
-                    .collect { _isSideBySideSupported.value = it }
+                combine(
+                        bouncerInteractor.isOneHandedModeSupported,
+                        bouncerInteractor.lastRecordedLockscreenTouchPosition,
+                        ::Pair,
+                    )
+                    .collect { (isOneHandedModeSupported, lastRecordedNotificationTouchPosition) ->
+                        _isOneHandedModeSupported.value = isOneHandedModeSupported
+                        if (
+                            isOneHandedModeSupported &&
+                                lastRecordedNotificationTouchPosition != null
+                        ) {
+                            bouncerInteractor.setPreferredBouncerInputSide(
+                                if (
+                                    lastRecordedNotificationTouchPosition <
+                                        applicationContext.resources.displayMetrics.widthPixels / 2
+                                ) {
+                                    BouncerInputSide.LEFT
+                                } else {
+                                    BouncerInputSide.RIGHT
+                                }
+                            )
+                        }
+                    }
+            }
+
+            launch {
+                bouncerInteractor.isUserSwitcherVisible.collect {
+                    _isUserSwitcherVisible.value = it
+                }
+            }
+
+            launch {
+                bouncerInteractor.preferredBouncerInputSide.collect {
+                    _isInputPreferredOnLeftSide.value = it == BouncerInputSide.LEFT
+                }
             }
 
             launch {
@@ -220,10 +260,6 @@
         }
     }
 
-    private fun isSideBySideSupported(authMethod: AuthMethodBouncerViewModel?): Boolean {
-        return isUserSwitcherVisible || authMethod !is PasswordBouncerViewModel
-    }
-
     private fun isFoldSplitRequired(authMethod: AuthMethodBouncerViewModel?): Boolean {
         return authMethod !is PasswordBouncerViewModel
     }
@@ -334,6 +370,29 @@
     }
 
     /**
+     * Notifies that double tap gesture was detected on the bouncer.
+     * [wasEventOnNonInputHalfOfScreen] is true when it happens on the side of the bouncer where the
+     * input UI is not present.
+     */
+    fun onDoubleTap(wasEventOnNonInputHalfOfScreen: Boolean) {
+        if (!wasEventOnNonInputHalfOfScreen) return
+        if (_isInputPreferredOnLeftSide.value) {
+            bouncerInteractor.setPreferredBouncerInputSide(BouncerInputSide.RIGHT)
+        } else {
+            bouncerInteractor.setPreferredBouncerInputSide(BouncerInputSide.LEFT)
+        }
+    }
+
+    /**
+     * Notifies that onDown was detected on the bouncer. [wasEventOnNonInputHalfOfScreen] is true
+     * when it happens on the side of the bouncer where the input UI is not present.
+     */
+    fun onDown(wasEventOnNonInputHalfOfScreen: Boolean) {
+        if (!wasEventOnNonInputHalfOfScreen) return
+        bouncerInteractor.onDown()
+    }
+
+    /**
      * Notifies that a key event has occurred.
      *
      * @return `true` when the [KeyEvent] was consumed as user input on bouncer; `false` otherwise.
@@ -344,6 +403,24 @@
             ?: false
     }
 
+    fun onActionButtonClicked(actionButtonModel: BouncerActionButtonModel) {
+        when (actionButtonModel) {
+            is BouncerActionButtonModel.EmergencyButtonModel -> {
+                bouncerHapticPlayer.playEmergencyButtonClickFeedback()
+                bouncerActionButtonInteractor.onEmergencyButtonClicked()
+            }
+            is BouncerActionButtonModel.ReturnToCallButtonModel -> {
+                bouncerActionButtonInteractor.onReturnToCallButtonClicked()
+            }
+        }
+    }
+
+    fun onActionButtonLongClicked(actionButtonModel: BouncerActionButtonModel) {
+        if (actionButtonModel is BouncerActionButtonModel.EmergencyButtonModel) {
+            bouncerActionButtonInteractor.onEmergencyButtonLongClicked()
+        }
+    }
+
     data class DialogViewModel(
         val text: String,
 
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 8639ee5..02161d2 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
@@ -20,21 +20,31 @@
 import androidx.compose.foundation.clickable
 import androidx.compose.foundation.gestures.Orientation
 import androidx.compose.foundation.interaction.MutableInteractionSource
+import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.foundation.layout.size
 import androidx.compose.material3.MaterialTheme
 import androidx.compose.material3.Text
 import androidx.compose.runtime.Composable
+import androidx.compose.runtime.DisposableEffect
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableIntStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.rememberCoroutineScope
 import androidx.compose.runtime.setValue
 import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.drawWithCache
+import androidx.compose.ui.geometry.CornerRadius
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.geometry.Size
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.res.colorResource
 import androidx.compose.ui.res.stringResource
 import androidx.compose.ui.unit.dp
 import androidx.lifecycle.compose.collectAsStateWithLifecycle
+import com.android.app.tracing.coroutines.launchTraced as launch
 import com.android.compose.PlatformSlider
+import com.android.compose.ui.graphics.drawInOverlay
 import com.android.systemui.Flags
 import com.android.systemui.brightness.shared.model.GammaBrightness
 import com.android.systemui.brightness.ui.viewmodel.BrightnessSliderViewModel
@@ -42,12 +52,13 @@
 import com.android.systemui.common.shared.model.Icon
 import com.android.systemui.common.shared.model.Text
 import com.android.systemui.common.ui.compose.Icon
+import com.android.systemui.compose.modifiers.sysuiResTag
 import com.android.systemui.haptics.slider.SeekableSliderTrackerConfig
 import com.android.systemui.haptics.slider.SliderHapticFeedbackConfig
 import com.android.systemui.haptics.slider.compose.ui.SliderHapticsViewModel
 import com.android.systemui.lifecycle.rememberViewModel
+import com.android.systemui.res.R
 import com.android.systemui.utils.PolicyRestriction
-import com.android.app.tracing.coroutines.launchTraced as launch
 
 @Composable
 private fun BrightnessSlider(
@@ -104,7 +115,7 @@
             }
         },
         modifier =
-            modifier.clickable(enabled = isRestricted) {
+            modifier.sysuiResTag("slider").clickable(enabled = isRestricted) {
                 if (restriction is PolicyRestriction.Restricted) {
                     onRestrictedClick(restriction)
                 }
@@ -127,27 +138,55 @@
     )
 }
 
+private val sliderBackgroundFrameSize = 8.dp
+
+private fun Modifier.sliderBackground(color: Color) = drawWithCache {
+    val offsetAround = sliderBackgroundFrameSize.toPx()
+    val newSize = Size(size.width + 2 * offsetAround, size.height + 2 * offsetAround)
+    val offset = Offset(-offsetAround, -offsetAround)
+    val cornerRadius = CornerRadius(offsetAround + size.height / 2)
+    onDrawBehind {
+        drawRoundRect(color = color, topLeft = offset, size = newSize, cornerRadius = cornerRadius)
+    }
+}
+
 @Composable
-fun BrightnessSliderContainer(viewModel: BrightnessSliderViewModel, modifier: Modifier = Modifier) {
-    val state by viewModel.currentBrightness.collectAsStateWithLifecycle()
-    val gamma = state.value
+fun BrightnessSliderContainer(
+    viewModel: BrightnessSliderViewModel,
+    modifier: Modifier = Modifier,
+    containerColor: Color = colorResource(R.color.shade_scrim_background_dark),
+) {
+    val gamma = viewModel.currentBrightness.value
     val coroutineScope = rememberCoroutineScope()
     val restriction by
         viewModel.policyRestriction.collectAsStateWithLifecycle(
             initialValue = PolicyRestriction.NoRestriction
         )
 
-    BrightnessSlider(
-        gammaValue = gamma,
-        valueRange = viewModel.minBrightness.value..viewModel.maxBrightness.value,
-        label = viewModel.label,
-        icon = viewModel.icon,
-        restriction = restriction,
-        onRestrictedClick = viewModel::showPolicyRestrictionDialog,
-        onDrag = { coroutineScope.launch { viewModel.onDrag(Drag.Dragging(GammaBrightness(it))) } },
-        onStop = { coroutineScope.launch { viewModel.onDrag(Drag.Stopped(GammaBrightness(it))) } },
-        modifier = modifier.fillMaxWidth(),
-        formatter = viewModel::formatValue,
-        hapticsViewModelFactory = viewModel.hapticsViewModelFactory,
-    )
+    DisposableEffect(Unit) { onDispose { viewModel.setIsDragging(false) } }
+
+    Box(modifier = modifier.fillMaxWidth().sysuiResTag("brightness_slider")) {
+        BrightnessSlider(
+            gammaValue = gamma,
+            valueRange = viewModel.minBrightness.value..viewModel.maxBrightness.value,
+            label = viewModel.label,
+            icon = viewModel.icon,
+            restriction = restriction,
+            onRestrictedClick = viewModel::showPolicyRestrictionDialog,
+            onDrag = {
+                viewModel.setIsDragging(true)
+                coroutineScope.launch { viewModel.onDrag(Drag.Dragging(GammaBrightness(it))) }
+            },
+            onStop = {
+                viewModel.setIsDragging(false)
+                coroutineScope.launch { viewModel.onDrag(Drag.Stopped(GammaBrightness(it))) }
+            },
+            modifier =
+                Modifier.then(if (viewModel.showMirror) Modifier.drawInOverlay() else Modifier)
+                    .sliderBackground(containerColor)
+                    .fillMaxWidth(),
+            formatter = viewModel::formatValue,
+            hapticsViewModelFactory = viewModel.hapticsViewModelFactory,
+        )
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/brightness/ui/viewmodel/BrightnessSliderViewModel.kt b/packages/SystemUI/src/com/android/systemui/brightness/ui/viewmodel/BrightnessSliderViewModel.kt
index 074ac50..a61ce8f 100644
--- a/packages/SystemUI/src/com/android/systemui/brightness/ui/viewmodel/BrightnessSliderViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/brightness/ui/viewmodel/BrightnessSliderViewModel.kt
@@ -16,36 +16,50 @@
 
 package com.android.systemui.brightness.ui.viewmodel
 
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.setValue
 import com.android.systemui.brightness.domain.interactor.BrightnessPolicyEnforcementInteractor
 import com.android.systemui.brightness.domain.interactor.ScreenBrightnessInteractor
 import com.android.systemui.brightness.shared.model.GammaBrightness
 import com.android.systemui.common.shared.model.ContentDescription
 import com.android.systemui.common.shared.model.Icon
 import com.android.systemui.common.shared.model.Text
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.haptics.slider.compose.ui.SliderHapticsViewModel
+import com.android.systemui.lifecycle.ExclusiveActivatable
+import com.android.systemui.lifecycle.Hydrator
 import com.android.systemui.res.R
+import com.android.systemui.settings.brightness.domain.interactor.BrightnessMirrorShowingInteractor
 import com.android.systemui.utils.PolicyRestriction
-import javax.inject.Inject
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.flow.SharingStarted
-import kotlinx.coroutines.flow.stateIn
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
 
-@SysUISingleton
+/**
+ * View Model for a brightness slider.
+ *
+ * If this brightness slider supports mirroring (show on top of current activity while dragging),
+ * then:
+ * * [showMirror] will be true while dragging
+ * * [BrightnessMirrorShowingInteractor.isShowing] will track if the mirror should show (for (other
+ *   parts of SystemUI to act accordingly).
+ */
 class BrightnessSliderViewModel
-@Inject
+@AssistedInject
 constructor(
     private val screenBrightnessInteractor: ScreenBrightnessInteractor,
     private val brightnessPolicyEnforcementInteractor: BrightnessPolicyEnforcementInteractor,
-    @Application private val applicationScope: CoroutineScope,
     val hapticsViewModelFactory: SliderHapticsViewModel.Factory,
-) {
-    val currentBrightness =
-        screenBrightnessInteractor.gammaBrightness.stateIn(
-            applicationScope,
-            SharingStarted.WhileSubscribed(),
+    private val brightnessMirrorShowingInteractor: BrightnessMirrorShowingInteractor,
+    @Assisted private val supportsMirroring: Boolean,
+) : ExclusiveActivatable() {
+
+    private val hydrator = Hydrator("BrightnessSliderViewModel.hydrator")
+
+    val currentBrightness by
+        hydrator.hydratedStateOf(
+            "currentBrightness",
             GammaBrightness(0),
+            screenBrightnessInteractor.gammaBrightness,
         )
 
     val maxBrightness = screenBrightnessInteractor.maxGammaBrightness
@@ -82,8 +96,26 @@
         // This is not finalized UI so using fixed string
         return "$percentage%"
     }
+
+    fun setIsDragging(dragging: Boolean) {
+        brightnessMirrorShowingInteractor.setMirrorShowing(dragging && supportsMirroring)
+    }
+
+    val showMirror by
+        hydrator.hydratedStateOf("showMirror", brightnessMirrorShowingInteractor.isShowing)
+
+    override suspend fun onActivated(): Nothing {
+        hydrator.activate()
+    }
+
+    @AssistedFactory
+    interface Factory {
+        fun create(supportsMirroring: Boolean): BrightnessSliderViewModel
+    }
 }
 
+fun BrightnessSliderViewModel.Factory.create() = create(supportsMirroring = true)
+
 /** Represents a drag event in a brightness slider. */
 sealed interface Drag {
     val brightness: GammaBrightness
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneTransitionInteractor.kt
index 7b8c19c..ec55401 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneTransitionInteractor.kt
@@ -73,7 +73,7 @@
     private var progressJob: Job? = null
 
     private val currentToState: KeyguardState
-        get() = internalTransitionInteractor.currentTransitionInfoInternal.value.to
+        get() = internalTransitionInteractor.currentTransitionInfoInternal().to
 
     /**
      * The next keyguard state to trigger when exiting [CommunalScenes.Communal]. This is only used
@@ -197,7 +197,7 @@
         val newTransition =
             TransitionInfo(
                 ownerName = this::class.java.simpleName,
-                from = internalTransitionInteractor.currentTransitionInfoInternal.value.to,
+                from = internalTransitionInteractor.currentTransitionInfoInternal().to,
                 to = state,
                 animator = null,
                 modeOnCanceled = TransitionModeOnCanceled.REVERSE,
@@ -273,7 +273,7 @@
     }
 
     private suspend fun startTransitionToGlanceableHub() {
-        val currentState = internalTransitionInteractor.currentTransitionInfoInternal.value.to
+        val currentState = internalTransitionInteractor.currentTransitionInfoInternal().to
         val newTransition =
             TransitionInfo(
                 ownerName = this::class.java.simpleName,
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/ResizeableItemFrameViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/ResizeableItemFrameViewModel.kt
index db4bee7..bde5d0f 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/ResizeableItemFrameViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/ResizeableItemFrameViewModel.kt
@@ -17,6 +17,7 @@
 
 import androidx.compose.foundation.gestures.AnchoredDraggableState
 import androidx.compose.foundation.gestures.DraggableAnchors
+import androidx.compose.foundation.gestures.snapTo
 import androidx.compose.runtime.snapshotFlow
 import com.android.app.tracing.coroutines.coroutineScopeTraced as coroutineScope
 import com.android.systemui.lifecycle.ExclusiveActivatable
@@ -81,6 +82,72 @@
             get() = roundDownToMultiple(getSpansForPx(minHeightPx))
     }
 
+    /** Check if widget can expanded based on current drag states */
+    fun canExpand(): Boolean {
+        return getNextAnchor(bottomDragState, moveUp = false) != null ||
+            getNextAnchor(topDragState, moveUp = true) != null
+    }
+
+    /** Check if widget can shrink based on current drag states */
+    fun canShrink(): Boolean {
+        return getNextAnchor(bottomDragState, moveUp = true) != null ||
+            getNextAnchor(topDragState, moveUp = false) != null
+    }
+
+    /** Get the next anchor value in the specified direction */
+    private fun getNextAnchor(state: AnchoredDraggableState<Int>, moveUp: Boolean): Int? {
+        var nextAnchor: Int? = null
+        var nextAnchorDiff = Int.MAX_VALUE
+        val currentValue = state.currentValue
+
+        for (i in 0 until state.anchors.size) {
+            val anchor = state.anchors.anchorAt(i) ?: continue
+            if (anchor == currentValue) continue
+
+            val diff =
+                if (moveUp) {
+                    currentValue - anchor
+                } else {
+                    anchor - currentValue
+                }
+
+            if (diff in 1..<nextAnchorDiff) {
+                nextAnchor = anchor
+                nextAnchorDiff = diff
+            }
+        }
+
+        return nextAnchor
+    }
+
+    /** Handle expansion to the next anchor */
+    suspend fun expandToNextAnchor() {
+        if (!canExpand()) return
+        val bottomAnchor = getNextAnchor(state = bottomDragState, moveUp = false)
+        if (bottomAnchor != null) {
+            bottomDragState.snapTo(bottomAnchor)
+            return
+        }
+        val topAnchor =
+            getNextAnchor(
+                state = topDragState,
+                moveUp = true, // Moving up to expand
+            )
+        topAnchor?.let { topDragState.snapTo(it) }
+    }
+
+    /** Handle shrinking to the next anchor */
+    suspend fun shrinkToNextAnchor() {
+        if (!canShrink()) return
+        val topAnchor = getNextAnchor(state = topDragState, moveUp = false)
+        if (topAnchor != null) {
+            topDragState.snapTo(topAnchor)
+            return
+        }
+        val bottomAnchor = getNextAnchor(state = bottomDragState, moveUp = true)
+        bottomAnchor?.let { bottomDragState.snapTo(it) }
+    }
+
     /**
      * The layout information necessary in order to calculate the pixel offsets of the drag anchor
      * points.
diff --git a/packages/SystemUI/src/com/android/systemui/complication/dagger/RegisteredComplicationsModule.java b/packages/SystemUI/src/com/android/systemui/complication/dagger/RegisteredComplicationsModule.java
index 6f1b098..9970c5d 100644
--- a/packages/SystemUI/src/com/android/systemui/complication/dagger/RegisteredComplicationsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/complication/dagger/RegisteredComplicationsModule.java
@@ -20,7 +20,6 @@
 import android.view.ViewGroup;
 
 import com.android.systemui.complication.ComplicationLayoutParams;
-import com.android.systemui.dagger.SystemUIBinder;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.flags.Flags;
@@ -32,8 +31,7 @@
 import javax.inject.Named;
 
 /**
- * Module for all components with corresponding dream layer complications registered in
- * {@link SystemUIBinder}.
+ * Module for all components with corresponding dream layer complications.
  */
 @Module(
         subcomponents = {
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSysUIComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSysUIComponent.java
index b71af69..b34a240 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSysUIComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSysUIComponent.java
@@ -32,7 +32,6 @@
         DependencyProvider.class,
         NotificationInsetsModule.class,
         QsFrameTranslateModule.class,
-        SystemUIBinder.class,
         SystemUIModule.class,
         SystemUICoreStartableModule.class,
         SysUIUnfoldModule.class,
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
index c6be0dd..609b733 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
@@ -35,8 +35,10 @@
 import com.android.systemui.dock.DockManagerImpl;
 import com.android.systemui.doze.DozeHost;
 import com.android.systemui.education.dagger.ContextualEducationModule;
+import com.android.systemui.emergency.EmergencyGestureModule;
 import com.android.systemui.inputdevice.tutorial.KeyboardTouchpadTutorialModule;
 import com.android.systemui.keyboard.shortcut.ShortcutHelperModule;
+import com.android.systemui.keyguard.dagger.KeyguardModule;
 import com.android.systemui.keyguard.ui.composable.blueprint.DefaultBlueprintModule;
 import com.android.systemui.keyguard.ui.view.layout.blueprints.KeyguardBlueprintModule;
 import com.android.systemui.keyguard.ui.view.layout.sections.KeyguardSectionsModule;
@@ -52,6 +54,7 @@
 import com.android.systemui.reardisplay.RearDisplayModule;
 import com.android.systemui.recents.Recents;
 import com.android.systemui.recents.RecentsImplementation;
+import com.android.systemui.recents.RecentsModule;
 import com.android.systemui.rotationlock.RotationLockModule;
 import com.android.systemui.rotationlock.RotationLockNewModule;
 import com.android.systemui.scene.SceneContainerFrameworkModule;
@@ -67,7 +70,9 @@
 import com.android.systemui.statusbar.NotificationLockscreenUserManagerImpl;
 import com.android.systemui.statusbar.NotificationShadeWindowController;
 import com.android.systemui.statusbar.SysuiStatusBarStateController;
+import com.android.systemui.statusbar.dagger.CentralSurfacesModule;
 import com.android.systemui.statusbar.dagger.StartCentralSurfacesModule;
+import com.android.systemui.statusbar.notification.dagger.ReferenceNotificationsModule;
 import com.android.systemui.statusbar.phone.CentralSurfaces;
 import com.android.systemui.statusbar.phone.DozeServiceHost;
 import com.android.systemui.statusbar.phone.HeadsUpModule;
@@ -119,12 +124,15 @@
         AccessibilityRepositoryModule.class,
         AospPolicyModule.class,
         BatterySaverModule.class,
+        CentralSurfacesModule.class,
         ClipboardOverlaySuppressionModule.class,
         CollapsedStatusBarFragmentStartableModule.class,
         ConnectingDisplayViewModel.StartableModule.class,
         DefaultBlueprintModule.class,
+        EmergencyGestureModule.class,
         GestureModule.class,
         HeadsUpModule.class,
+        KeyguardModule.class,
         KeyboardShortcutsModule.class,
         KeyguardBlueprintModule.class,
         KeyguardSectionsModule.class,
@@ -137,6 +145,8 @@
         PowerModule.class,
         QSModule.class,
         RearDisplayModule.class,
+        RecentsModule.class,
+        ReferenceNotificationsModule.class,
         ReferenceScreenshotModule.class,
         RotationLockModule.class,
         RotationLockNewModule.class,
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
index 17f1961..580896c 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
@@ -65,7 +65,6 @@
         DependencyProvider.class,
         NotificationInsetsModule.class,
         QsFrameTranslateModule.class,
-        SystemUIBinder.class,
         SystemUIModule.class,
         SystemUICoreStartableModule.class,
         ReferenceSystemUIModule.class})
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java
deleted file mode 100644
index 2f041ac..0000000
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright (C) 2019 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.dagger;
-
-import com.android.systemui.keyguard.dagger.KeyguardModule;
-import com.android.systemui.recents.RecentsModule;
-import com.android.systemui.statusbar.dagger.CentralSurfacesModule;
-
-import dagger.Module;
-
-/**
- * SystemUI objects that are injectable should go here.
- */
-@Module(includes = {
-        RecentsModule.class,
-        CentralSurfacesModule.class,
-        KeyguardModule.class,
-})
-public abstract class SystemUIBinder {
-}
diff --git a/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayWindowPropertiesRepository.kt b/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayWindowPropertiesRepository.kt
index 7253621..80eb9ee 100644
--- a/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayWindowPropertiesRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayWindowPropertiesRepository.kt
@@ -19,7 +19,9 @@
 import android.annotation.SuppressLint
 import android.content.Context
 import android.view.Display
+import android.view.LayoutInflater
 import android.view.WindowManager
+import com.android.app.tracing.coroutines.launchTraced as launch
 import com.android.systemui.CoreStartable
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Background
@@ -30,9 +32,7 @@
 import com.google.common.collect.Table
 import java.io.PrintWriter
 import javax.inject.Inject
-import kotlinx.coroutines.CoroutineName
 import kotlinx.coroutines.CoroutineScope
-import com.android.app.tracing.coroutines.launchTraced as launch
 
 /** Provides per display instances of [DisplayWindowProperties]. */
 interface DisplayWindowPropertiesRepository {
@@ -55,6 +55,7 @@
     @Background private val backgroundApplicationScope: CoroutineScope,
     private val globalContext: Context,
     private val globalWindowManager: WindowManager,
+    private val globalLayoutInflater: LayoutInflater,
     private val displayRepository: DisplayRepository,
 ) : DisplayWindowPropertiesRepository, CoreStartable {
 
@@ -93,12 +94,14 @@
                 windowType = windowType,
                 context = globalContext,
                 windowManager = globalWindowManager,
+                layoutInflater = globalLayoutInflater,
             )
         } else {
             val context = createWindowContext(display, windowType)
             @SuppressLint("NonInjectedService") // Need to manually get the service
             val windowManager = context.getSystemService(WindowManager::class.java) as WindowManager
-            DisplayWindowProperties(displayId, windowType, context, windowManager)
+            val layoutInflater = LayoutInflater.from(context)
+            DisplayWindowProperties(displayId, windowType, context, windowManager, layoutInflater)
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/display/shared/model/DisplayWindowProperties.kt b/packages/SystemUI/src/com/android/systemui/display/shared/model/DisplayWindowProperties.kt
index 6acc296..3f1c088 100644
--- a/packages/SystemUI/src/com/android/systemui/display/shared/model/DisplayWindowProperties.kt
+++ b/packages/SystemUI/src/com/android/systemui/display/shared/model/DisplayWindowProperties.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.display.shared.model
 
 import android.content.Context
+import android.view.LayoutInflater
 import android.view.WindowManager
 
 /** Represents a display specific group of window related properties. */
@@ -40,4 +41,7 @@
      * associated with this instance.
      */
     val windowManager: WindowManager,
+
+    /** The [LayoutInflater] to be used with the associated [Context]. */
+    val layoutInflater: LayoutInflater,
 )
diff --git a/packages/SystemUI/src/com/android/systemui/education/ui/view/ContextualEduUiCoordinator.kt b/packages/SystemUI/src/com/android/systemui/education/ui/view/ContextualEduUiCoordinator.kt
index 6baffdd..2a3729b 100644
--- a/packages/SystemUI/src/com/android/systemui/education/ui/view/ContextualEduUiCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/education/ui/view/ContextualEduUiCoordinator.kt
@@ -34,6 +34,8 @@
 import com.android.systemui.education.ui.viewmodel.ContextualEduToastViewModel
 import com.android.systemui.education.ui.viewmodel.ContextualEduViewModel
 import com.android.systemui.inputdevice.tutorial.ui.view.KeyboardTouchpadTutorialActivity
+import com.android.systemui.inputdevice.tutorial.ui.view.KeyboardTouchpadTutorialActivity.Companion.INTENT_TUTORIAL_ENTRY_POINT_CONTEXTUAL_EDU
+import com.android.systemui.inputdevice.tutorial.ui.view.KeyboardTouchpadTutorialActivity.Companion.INTENT_TUTORIAL_ENTRY_POINT_KEY
 import com.android.systemui.res.R
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
@@ -99,7 +101,7 @@
                 CHANNEL_ID,
                 context.getString(com.android.internal.R.string.android_system_label),
                 // Make it as silent notification
-                NotificationManager.IMPORTANCE_LOW
+                NotificationManager.IMPORTANCE_LOW,
             )
         notificationManager.createNotificationChannel(channel)
     }
@@ -114,7 +116,7 @@
         val extras = Bundle()
         extras.putString(
             Notification.EXTRA_SUBSTITUTE_APP_NAME,
-            context.getString(com.android.internal.R.string.android_system_label)
+            context.getString(com.android.internal.R.string.android_system_label),
         )
 
         val notification =
@@ -131,7 +133,7 @@
             TAG,
             NOTIFICATION_ID,
             notification,
-            UserHandle.of(model.userId)
+            UserHandle.of(model.userId),
         )
     }
 
@@ -140,12 +142,16 @@
             Intent(context, KeyboardTouchpadTutorialActivity::class.java).apply {
                 addCategory(Intent.CATEGORY_DEFAULT)
                 flags = Intent.FLAG_ACTIVITY_NEW_TASK
+                putExtra(
+                    INTENT_TUTORIAL_ENTRY_POINT_KEY,
+                    INTENT_TUTORIAL_ENTRY_POINT_CONTEXTUAL_EDU,
+                )
             }
         return PendingIntent.getActivity(
             context,
             /* requestCode= */ 0,
             intent,
-            PendingIntent.FLAG_IMMUTABLE
+            PendingIntent.FLAG_IMMUTABLE,
         )
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index 61832875..b431636 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -255,11 +255,6 @@
     val WM_ENABLE_SHELL_TRANSITIONS =
         sysPropBooleanFlag("persist.wm.debug.shell_transit", default = true)
 
-    // TODO(b/256873975): Tracking Bug
-    @JvmField
-    @Keep
-    val WM_BUBBLE_BAR = sysPropBooleanFlag("persist.wm.debug.bubble_bar", default = false)
-
     // TODO(b/254513207): Tracking Bug to delete
     @Keep
     @JvmField
diff --git a/packages/SystemUI/src/com/android/systemui/haptics/slider/HapticSlider.kt b/packages/SystemUI/src/com/android/systemui/haptics/slider/HapticSlider.kt
new file mode 100644
index 0000000..1d226fd
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/haptics/slider/HapticSlider.kt
@@ -0,0 +1,41 @@
+/*
+ * 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.haptics.slider
+
+sealed interface HapticSlider {
+
+    val min: Float
+    val max: Float
+
+    class SeekBar(val seekBar: android.widget.SeekBar) : HapticSlider {
+
+        override val min: Float
+            get() = seekBar.min.toFloat()
+
+        override val max: Float
+            get() = seekBar.max.toFloat()
+    }
+
+    class Slider(val slider: com.google.android.material.slider.Slider) : HapticSlider {
+
+        override val min: Float
+            get() = slider.valueFrom
+
+        override val max: Float
+            get() = slider.valueTo
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/haptics/slider/SeekbarHapticPlugin.kt b/packages/SystemUI/src/com/android/systemui/haptics/slider/HapticSliderPlugin.kt
similarity index 83%
rename from packages/SystemUI/src/com/android/systemui/haptics/slider/SeekbarHapticPlugin.kt
rename to packages/SystemUI/src/com/android/systemui/haptics/slider/HapticSliderPlugin.kt
index f6d7e15..16c53b1 100644
--- a/packages/SystemUI/src/com/android/systemui/haptics/slider/SeekbarHapticPlugin.kt
+++ b/packages/SystemUI/src/com/android/systemui/haptics/slider/HapticSliderPlugin.kt
@@ -18,30 +18,30 @@
 
 import android.view.MotionEvent
 import android.view.VelocityTracker
-import android.widget.SeekBar
 import androidx.annotation.VisibleForTesting
+import com.android.app.tracing.coroutines.launchTraced as launch
 import com.android.systemui.statusbar.VibratorHelper
 import com.android.systemui.util.time.SystemClock
 import com.google.android.msdl.domain.MSDLPlayer
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Job
 import kotlinx.coroutines.delay
-import com.android.app.tracing.coroutines.launchTraced as launch
 
 /**
- * A plugin added to a manager of a [android.widget.SeekBar] that adds dynamic haptic feedback.
+ * A plugin added to a manager of a [HapticSlider] that adds dynamic haptic feedback.
  *
  * A [SliderStateProducer] is used as the producer of slider events, a
  * [SliderHapticFeedbackProvider] is used as the listener of slider states to play haptic feedback
  * depending on the state, and a [SliderStateTracker] is used as the state machine handler that
  * tracks and manipulates the slider state.
  */
-class SeekbarHapticPlugin
+class HapticSliderPlugin
 @JvmOverloads
 constructor(
     vibratorHelper: VibratorHelper,
     msdlPlayer: MSDLPlayer,
     systemClock: SystemClock,
+    private val slider: HapticSlider,
     sliderHapticFeedbackConfig: SliderHapticFeedbackConfig = SliderHapticFeedbackConfig(),
     private val sliderTrackerConfig: SeekableSliderTrackerConfig = SeekableSliderTrackerConfig(),
 ) {
@@ -128,46 +128,42 @@
         }
     }
 
-    /** onStartTrackingTouch event from the slider's [android.widget.SeekBar] */
-    fun onStartTrackingTouch(seekBar: SeekBar) {
+    /** onStartTrackingTouch event from the slider. */
+    fun onStartTrackingTouch() {
         if (isTracking) {
             sliderEventProducer.onStartTracking(true)
         }
     }
 
-    /** onProgressChanged event from the slider's [android.widget.SeekBar] */
-    fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
+    /** onProgressChanged event from the slider's. */
+    fun onProgressChanged(progress: Int, fromUser: Boolean) {
         if (isTracking) {
             if (sliderTracker?.currentState == SliderState.IDLE && !fromUser) {
                 // This case translates to the slider starting to track program changes
-                sliderEventProducer.resetWithProgress(normalizeProgress(seekBar, progress))
+                sliderEventProducer.resetWithProgress(normalizeProgress(slider, progress))
                 sliderEventProducer.onStartTracking(false)
             } else {
-                sliderEventProducer.onProgressChanged(
-                    fromUser,
-                    normalizeProgress(seekBar, progress),
-                )
+                sliderEventProducer.onProgressChanged(fromUser, normalizeProgress(slider, progress))
             }
         }
     }
 
     /**
-     * Normalize the integer progress of a SeekBar to the range from 0F to 1F.
+     * Normalize the integer progress of a HapticSlider to the range from 0F to 1F.
      *
-     * @param[seekBar] The SeekBar that reports a progress.
-     * @param[progress] The integer progress of the SeekBar within its min and max values.
+     * @param[slider] The HapticSlider that reports a progress.
+     * @param[progress] The integer progress of the HapticSlider within its min and max values.
      * @return The progress in the range from 0F to 1F.
      */
-    private fun normalizeProgress(seekBar: SeekBar, progress: Int): Float {
-        if (seekBar.max == seekBar.min) {
+    private fun normalizeProgress(slider: HapticSlider, progress: Int): Float {
+        if (slider.max == slider.min) {
             return 1.0f
         }
-        val range = seekBar.max - seekBar.min
-        return (progress - seekBar.min) / range.toFloat()
+        return (progress - slider.min) / (slider.max - slider.min)
     }
 
-    /** onStopTrackingTouch event from the slider's [android.widget.SeekBar] */
-    fun onStopTrackingTouch(seekBar: SeekBar) {
+    /** onStopTrackingTouch event from the slider. */
+    fun onStopTrackingTouch() {
         if (isTracking) {
             sliderEventProducer.onStopTracking(true)
         }
diff --git a/packages/SystemUI/src/com/android/systemui/haptics/slider/HapticSliderViewBinder.kt b/packages/SystemUI/src/com/android/systemui/haptics/slider/HapticSliderViewBinder.kt
index ca6c8da..f43682d 100644
--- a/packages/SystemUI/src/com/android/systemui/haptics/slider/HapticSliderViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/haptics/slider/HapticSliderViewBinder.kt
@@ -23,11 +23,11 @@
 
 object HapticSliderViewBinder {
     /**
-     * Binds a [SeekbarHapticPlugin] to a [View]. The binded view should be a
+     * Binds a [HapticSliderPlugin] to a [View]. The binded view should be a
      * [android.widget.SeekBar] or a container of a [android.widget.SeekBar]
      */
     @JvmStatic
-    fun bind(view: View?, plugin: SeekbarHapticPlugin) {
+    fun bind(view: View?, plugin: HapticSliderPlugin) {
         view?.repeatWhenAttached {
             plugin.startInScope(lifecycleScope)
             try {
diff --git a/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderHapticFeedbackConfig.kt b/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderHapticFeedbackConfig.kt
index 24dd04d..da124de 100644
--- a/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderHapticFeedbackConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderHapticFeedbackConfig.kt
@@ -47,4 +47,6 @@
     @FloatRange(from = 0.0, to = 1.0) val lowerBookendScale: Float = 0.05f,
     /** Exponent for power function compensation */
     @FloatRange(from = 0.0, fromInclusive = false) val exponent: Float = 1f / 0.89f,
+    /** The step-size that defines the slider quantization. Zero represents a continuous slider */
+    @FloatRange(from = 0.0) val sliderStepSize: Float = 0f,
 )
diff --git a/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderHapticFeedbackProvider.kt b/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderHapticFeedbackProvider.kt
index bc4f531..de6697b 100644
--- a/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderHapticFeedbackProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderHapticFeedbackProvider.kt
@@ -30,6 +30,7 @@
 import kotlin.math.abs
 import kotlin.math.min
 import kotlin.math.pow
+import kotlin.math.round
 
 /**
  * Listener of slider events that triggers haptic feedback.
@@ -124,14 +125,45 @@
         val deltaProgress = abs(normalizedSliderProgress - dragTextureLastProgress)
         if (deltaProgress < config.deltaProgressForDragThreshold) return
 
+        // Check if the progress is a discrete step so haptics can be delivered
+        if (
+            config.sliderStepSize > 0 &&
+                !normalizedSliderProgress.isDiscreteStep(config.sliderStepSize)
+        ) {
+            return
+        }
+
         val powerScale = scaleOnDragTexture(absoluteVelocity, normalizedSliderProgress)
 
         // Deliver haptic feedback
-        performContinuousSliderDragVibration(powerScale)
+        when {
+            config.sliderStepSize == 0f -> performContinuousSliderDragVibration(powerScale)
+            config.sliderStepSize > 0f -> performDiscreteSliderDragVibration(powerScale)
+        }
         dragTextureLastTime = currentTime
         dragTextureLastProgress = normalizedSliderProgress
     }
 
+    private fun Float.isDiscreteStep(stepSize: Float, epsilon: Float = 0.001f): Boolean {
+        if (stepSize <= 0f) return false
+        val division = this / stepSize
+        return abs(division - round(division)) < epsilon
+    }
+
+    private fun performDiscreteSliderDragVibration(scale: Float) {
+        if (Flags.msdlFeedback()) {
+            val properties =
+                InteractionProperties.DynamicVibrationScale(scale, VIBRATION_ATTRIBUTES_PIPELINING)
+            msdlPlayer.playToken(MSDLToken.DRAG_INDICATOR_DISCRETE, properties)
+        } else {
+            val effect =
+                VibrationEffect.startComposition()
+                    .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, scale)
+                    .compose()
+            vibratorHelper.vibrate(effect, VIBRATION_ATTRIBUTES_PIPELINING)
+        }
+    }
+
     private fun performContinuousSliderDragVibration(scale: Float) {
         if (Flags.msdlFeedback()) {
             val properties =
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeUserActionsViewModelKosmos.kt b/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderQuantization.kt
similarity index 63%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeUserActionsViewModelKosmos.kt
copy to packages/SystemUI/src/com/android/systemui/haptics/slider/SliderQuantization.kt
index 06592b1..033d55c 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeUserActionsViewModelKosmos.kt
+++ b/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderQuantization.kt
@@ -14,13 +14,13 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.ui.viewmodel
+package com.android.systemui.haptics.slider
 
-import com.android.systemui.kosmos.Kosmos
+interface SliderQuantization {
+    /** What is the step size between discrete steps of the slider */
+    val stepSize: Float
 
-val Kosmos.quickSettingsShadeUserActionsViewModel: QuickSettingsShadeUserActionsViewModel by
-    Kosmos.Fixture {
-        QuickSettingsShadeUserActionsViewModel(
-            quickSettingsContainerViewModel = quickSettingsContainerViewModel,
-        )
-    }
+    data class Continuous(override val stepSize: Float = Float.MIN_VALUE) : SliderQuantization
+
+    data class Discrete(override val stepSize: Float) : SliderQuantization
+}
diff --git a/packages/SystemUI/src/com/android/systemui/haptics/slider/compose/ui/SliderHapticsViewModel.kt b/packages/SystemUI/src/com/android/systemui/haptics/slider/compose/ui/SliderHapticsViewModel.kt
index de24259..7fa83c6 100644
--- a/packages/SystemUI/src/com/android/systemui/haptics/slider/compose/ui/SliderHapticsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/haptics/slider/compose/ui/SliderHapticsViewModel.kt
@@ -143,18 +143,20 @@
             SliderEventType.STARTED_TRACKING_TOUCH -> {
                 startingProgress = normalized
                 currentSliderEventType = SliderEventType.PROGRESS_CHANGE_BY_USER
+                sliderStateProducer.onProgressChanged(true, normalized)
             }
             SliderEventType.PROGRESS_CHANGE_BY_USER -> {
-                velocityTracker.addPosition(System.currentTimeMillis(), normalized.toOffset())
+                addVelocityDataPoint(value)
                 currentSliderEventType = SliderEventType.PROGRESS_CHANGE_BY_USER
                 sliderStateProducer.onProgressChanged(true, normalized)
             }
             SliderEventType.STARTED_TRACKING_PROGRAM -> {
                 startingProgress = normalized
                 currentSliderEventType = SliderEventType.PROGRESS_CHANGE_BY_PROGRAM
+                sliderStateProducer.onProgressChanged(false, normalized)
             }
             SliderEventType.PROGRESS_CHANGE_BY_PROGRAM -> {
-                velocityTracker.addPosition(System.currentTimeMillis(), normalized.toOffset())
+                addVelocityDataPoint(value)
                 currentSliderEventType = SliderEventType.PROGRESS_CHANGE_BY_PROGRAM
                 sliderStateProducer.onProgressChanged(false, normalized)
             }
@@ -162,6 +164,11 @@
         }
     }
 
+    fun addVelocityDataPoint(value: Float) {
+        val normalized = value.normalize()
+        velocityTracker.addPosition(System.currentTimeMillis(), normalized.toOffset())
+    }
+
     fun onValueChangeEnded() {
         when (currentSliderEventType) {
             SliderEventType.STARTED_TRACKING_PROGRAM,
@@ -174,8 +181,10 @@
         velocityTracker.resetTracking()
     }
 
+    private fun ClosedFloatingPointRange<Float>.length(): Float = endInclusive - start
+
     private fun Float.normalize(): Float =
-        (this / (sliderRange.endInclusive - sliderRange.start)).coerceIn(0f, 1f)
+        ((this - sliderRange.start) / sliderRange.length()).coerceIn(0f, 1f)
 
     private fun Float.toOffset(): Offset =
         when (orientation) {
diff --git a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/data/repository/TutorialSchedulerRepository.kt b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/data/repository/TutorialSchedulerRepository.kt
index d8d4bd6..a89ec70 100644
--- a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/data/repository/TutorialSchedulerRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/data/repository/TutorialSchedulerRepository.kt
@@ -17,7 +17,6 @@
 package com.android.systemui.inputdevice.tutorial.data.repository
 
 import android.content.Context
-import androidx.annotation.VisibleForTesting
 import androidx.datastore.core.DataStore
 import androidx.datastore.preferences.core.Preferences
 import androidx.datastore.preferences.core.edit
@@ -37,12 +36,12 @@
 class TutorialSchedulerRepository(
     private val applicationContext: Context,
     backgroundScope: CoroutineScope,
-    dataStoreName: String
+    dataStoreName: String,
 ) {
     @Inject
     constructor(
         @Application applicationContext: Context,
-        @Background backgroundScope: CoroutineScope
+        @Background backgroundScope: CoroutineScope,
     ) : this(applicationContext, backgroundScope, dataStoreName = DATASTORE_NAME)
 
     private val Context.dataStore: DataStore<Preferences> by
@@ -73,7 +72,7 @@
     private fun getSchedulerInfo(pref: Preferences): Map<DeviceType, DeviceSchedulerInfo> {
         return mapOf(
             DeviceType.KEYBOARD to getDeviceSchedulerInfo(pref, DeviceType.KEYBOARD),
-            DeviceType.TOUCHPAD to getDeviceSchedulerInfo(pref, DeviceType.TOUCHPAD)
+            DeviceType.TOUCHPAD to getDeviceSchedulerInfo(pref, DeviceType.TOUCHPAD),
         )
     }
 
@@ -89,8 +88,7 @@
     private fun getConnectKey(device: DeviceType) =
         longPreferencesKey(device.name + CONNECT_TIME_SUFFIX)
 
-    @VisibleForTesting
-    suspend fun clearDataStore() {
+    suspend fun clear() {
         applicationContext.dataStore.edit { it.clear() }
     }
 
@@ -103,5 +101,5 @@
 
 enum class DeviceType {
     KEYBOARD,
-    TOUCHPAD
+    TOUCHPAD,
 }
diff --git a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/domain/interactor/TutorialSchedulerInteractor.kt b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/domain/interactor/TutorialSchedulerInteractor.kt
index 3b4d00d..4a369e7 100644
--- a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/domain/interactor/TutorialSchedulerInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/domain/interactor/TutorialSchedulerInteractor.kt
@@ -24,7 +24,10 @@
 import com.android.systemui.inputdevice.tutorial.data.repository.DeviceType.TOUCHPAD
 import com.android.systemui.inputdevice.tutorial.data.repository.TutorialSchedulerRepository
 import com.android.systemui.keyboard.data.repository.KeyboardRepository
+import com.android.systemui.statusbar.commandline.Command
+import com.android.systemui.statusbar.commandline.CommandRegistry
 import com.android.systemui.touchpad.data.repository.TouchpadRepository
+import java.io.PrintWriter
 import java.time.Duration
 import java.time.Instant
 import javax.inject.Inject
@@ -37,6 +40,7 @@
 import kotlinx.coroutines.flow.flow
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.merge
+import kotlinx.coroutines.runBlocking
 
 /**
  * When the first time a keyboard or touchpad is connected, wait for [LAUNCH_DELAY], and as soon as
@@ -50,7 +54,12 @@
     touchpadRepository: TouchpadRepository,
     private val repo: TutorialSchedulerRepository,
     private val logger: InputDeviceTutorialLogger,
+    commandRegistry: CommandRegistry,
 ) {
+    init {
+        commandRegistry.registerCommand(COMMAND) { TutorialCommand() }
+    }
+
     private val isAnyDeviceConnected =
         mapOf(
             KEYBOARD to keyboardRepository.isAnyKeyboardConnected,
@@ -118,8 +127,40 @@
         return LAUNCH_DELAY.minus(elapsed).toKotlinDuration()
     }
 
+    inner class TutorialCommand : Command {
+        override fun execute(pw: PrintWriter, args: List<String>) {
+            if (args.isEmpty()) {
+                help(pw)
+                return
+            }
+            when (args[0]) {
+                "clear" ->
+                    runBlocking {
+                        repo.clear()
+                        pw.println("Tutorial scheduler reset")
+                    }
+                "info" ->
+                    runBlocking {
+                        pw.println("Keyboard connect time = ${repo.firstConnectionTime(KEYBOARD)}")
+                        pw.println("         launch time = ${repo.launchTime(KEYBOARD)}")
+                        pw.println("Touchpad connect time = ${repo.firstConnectionTime(TOUCHPAD)}")
+                        pw.println("         launch time = ${repo.launchTime(TOUCHPAD)}")
+                    }
+                else -> help(pw)
+            }
+        }
+
+        override fun help(pw: PrintWriter) {
+            pw.println("Usage: adb shell cmd statusbar $COMMAND <command>")
+            pw.println("Available commands:")
+            pw.println("  clear")
+            pw.println("  info")
+        }
+    }
+
     companion object {
         const val TAG = "TutorialSchedulerInteractor"
+        const val COMMAND = "peripheral_tutorial"
         private val DEFAULT_LAUNCH_DELAY_SEC = 72.hours.inWholeSeconds
         private val LAUNCH_DELAY: Duration
             get() =
diff --git a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/composable/ActionTutorialContent.kt b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/composable/ActionTutorialContent.kt
index 3d2baee..6dd56de 100644
--- a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/composable/ActionTutorialContent.kt
+++ b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/composable/ActionTutorialContent.kt
@@ -16,10 +16,9 @@
 
 package com.android.systemui.inputdevice.tutorial.ui.composable
 
+import android.content.res.Configuration
 import androidx.annotation.RawRes
-import androidx.annotation.StringRes
-import androidx.compose.animation.AnimatedVisibility
-import androidx.compose.animation.fadeIn
+import androidx.compose.animation.core.animateFloatAsState
 import androidx.compose.foundation.background
 import androidx.compose.foundation.layout.Arrangement
 import androidx.compose.foundation.layout.Column
@@ -34,8 +33,11 @@
 import androidx.compose.material3.MaterialTheme
 import androidx.compose.material3.Text
 import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.graphicsLayer
+import androidx.compose.ui.platform.LocalConfiguration
 import androidx.compose.ui.res.stringResource
 import androidx.compose.ui.unit.dp
 import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState.Finished
@@ -66,42 +68,70 @@
                 .safeDrawingPadding()
                 .padding(start = 48.dp, top = 100.dp, end = 48.dp, bottom = 8.dp),
     ) {
-        Row(modifier = Modifier.fillMaxWidth().weight(1f)) {
-            TutorialDescription(
-                titleTextId =
-                    if (actionState is Finished) config.strings.titleSuccessResId
-                    else config.strings.titleResId,
-                titleColor = config.colors.title,
-                bodyTextId =
-                    if (actionState is Finished) config.strings.bodySuccessResId
-                    else config.strings.bodyResId,
-                modifier = Modifier.weight(1f),
-            )
-            Spacer(modifier = Modifier.width(76.dp))
-            TutorialAnimation(
-                actionState,
-                config,
-                modifier = Modifier.weight(1f).padding(top = 8.dp),
-            )
+        when (LocalConfiguration.current.orientation) {
+            Configuration.ORIENTATION_LANDSCAPE -> {
+                HorizontalDescriptionAndAnimation(actionState, config, Modifier.weight(1f))
+            }
+            else -> {
+                VerticalDescriptionAndAnimation(actionState, config, Modifier.weight(1f))
+            }
         }
-        AnimatedVisibility(visible = actionState is Finished, enter = fadeIn()) {
-            DoneButton(onDoneButtonClicked = onDoneButtonClicked)
-        }
+        val buttonAlpha by animateFloatAsState(if (actionState is Finished) 1f else 0f)
+        DoneButton(onDoneButtonClicked, Modifier.graphicsLayer { alpha = buttonAlpha })
+    }
+}
+
+@Composable
+private fun HorizontalDescriptionAndAnimation(
+    actionState: TutorialActionState,
+    config: TutorialScreenConfig,
+    modifier: Modifier = Modifier,
+) {
+    Row(modifier = modifier.fillMaxWidth()) {
+        TutorialDescription(actionState, config, modifier = Modifier.weight(1f))
+        Spacer(modifier = Modifier.width(70.dp))
+        TutorialAnimation(actionState, config, modifier = Modifier.weight(1f))
+    }
+}
+
+@Composable
+private fun VerticalDescriptionAndAnimation(
+    actionState: TutorialActionState,
+    config: TutorialScreenConfig,
+    modifier: Modifier = Modifier,
+) {
+    Column(modifier = modifier.fillMaxWidth().padding(horizontal = 40.dp, vertical = 40.dp)) {
+        Spacer(modifier = Modifier.weight(0.1f))
+        TutorialDescription(
+            actionState,
+            config,
+            modifier =
+                Modifier.weight(0.2f)
+                    // extra padding to better align with animation which has embedded padding
+                    .padding(horizontal = 15.dp),
+        )
+        Spacer(modifier = Modifier.width(70.dp))
+        TutorialAnimation(actionState, config, modifier = Modifier.weight(1f))
     }
 }
 
 @Composable
 fun TutorialDescription(
-    @StringRes titleTextId: Int,
-    titleColor: Color,
-    @StringRes bodyTextId: Int,
+    actionState: TutorialActionState,
+    config: TutorialScreenConfig,
     modifier: Modifier = Modifier,
 ) {
+    val (titleTextId, bodyTextId) =
+        if (actionState is Finished) {
+            config.strings.titleSuccessResId to config.strings.bodySuccessResId
+        } else {
+            config.strings.titleResId to config.strings.bodyResId
+        }
     Column(verticalArrangement = Arrangement.Top, modifier = modifier) {
         Text(
             text = stringResource(id = titleTextId),
             style = MaterialTheme.typography.displayLarge,
-            color = titleColor,
+            color = config.colors.title,
         )
         Spacer(modifier = Modifier.height(16.dp))
         Text(
diff --git a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/composable/TutorialAnimation.kt b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/composable/TutorialAnimation.kt
index 720c01f..2be619b 100644
--- a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/composable/TutorialAnimation.kt
+++ b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/composable/TutorialAnimation.kt
@@ -24,11 +24,13 @@
 import androidx.compose.animation.fadeOut
 import androidx.compose.animation.togetherWith
 import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.saveable.rememberSaveable
+import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.node.Ref
 import androidx.compose.ui.util.lerp
@@ -49,7 +51,7 @@
     config: TutorialScreenConfig,
     modifier: Modifier = Modifier,
 ) {
-    Box(modifier = modifier.fillMaxWidth()) {
+    Box(contentAlignment = Alignment.Center, modifier = modifier.fillMaxWidth()) {
         AnimatedContent(
             targetState = actionState::class,
             transitionSpec = {
@@ -97,6 +99,7 @@
         composition = composition,
         progress = { progress },
         dynamicProperties = animationProperties,
+        modifier = Modifier.fillMaxSize(),
     )
 }
 
@@ -112,6 +115,7 @@
         composition = composition,
         progress = { progress },
         dynamicProperties = animationProperties,
+        modifier = Modifier.fillMaxSize(),
     )
 }
 
@@ -142,6 +146,7 @@
         composition = composition,
         progress = { lerp(start = startProgress, stop = endProgress, fraction = progress) },
         dynamicProperties = animationProperties,
+        modifier = Modifier.fillMaxSize(),
     )
 }
 
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutHelper.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutHelper.kt
index 5cade68..d537056 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutHelper.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutHelper.kt
@@ -52,8 +52,10 @@
 import androidx.compose.foundation.verticalScroll
 import androidx.compose.material.icons.Icons
 import androidx.compose.material.icons.automirrored.filled.OpenInNew
+import androidx.compose.material.icons.filled.Add
 import androidx.compose.material.icons.filled.ExpandMore
 import androidx.compose.material.icons.filled.Search
+import androidx.compose.material.icons.filled.Tune
 import androidx.compose.material3.CenterAlignedTopAppBar
 import androidx.compose.material3.ExperimentalMaterial3Api
 import androidx.compose.material3.HorizontalDivider
@@ -69,6 +71,7 @@
 import androidx.compose.material3.TopAppBarDefaults
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.derivedStateOf
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
@@ -169,6 +172,7 @@
             selectedCategoryType,
             onCategorySelected = { selectedCategoryType = it },
             onKeyboardSettingsClicked,
+            shortcutsUiState.isShortcutCustomizerFlagEnabled,
         )
     }
 }
@@ -357,10 +361,29 @@
     selectedCategoryType: ShortcutCategoryType?,
     onCategorySelected: (ShortcutCategoryType?) -> Unit,
     onKeyboardSettingsClicked: () -> Unit,
+    isShortcutCustomizerFlagEnabled: Boolean,
 ) {
     val selectedCategory = categories.fastFirstOrNull { it.type == selectedCategoryType }
+    var isCustomizeModeEntered by remember { mutableStateOf(false) }
+    val isCustomizing by
+        remember(isCustomizeModeEntered, isShortcutCustomizerFlagEnabled) {
+            derivedStateOf { isCustomizeModeEntered && isCustomizeModeEntered }
+        }
+
     Column(modifier = modifier.fillMaxSize().padding(horizontal = 24.dp)) {
-        TitleBar()
+        Row(modifier = Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically) {
+            Box(modifier = Modifier.padding(start = 202.dp).width(412.dp)) {
+                TitleBar(isCustomizing)
+            }
+            Spacer(modifier = Modifier.weight(1f))
+            if (isShortcutCustomizerFlagEnabled) {
+                if (isCustomizeModeEntered) {
+                    DoneButton(onClick = { isCustomizeModeEntered = false })
+                } else {
+                    CustomizeButton(onClick = { isCustomizeModeEntered = true })
+                }
+            }
+        }
         Spacer(modifier = Modifier.height(12.dp))
         Row(Modifier.fillMaxWidth()) {
             StartSidePanel(
@@ -372,13 +395,46 @@
                 onCategoryClicked = { onCategorySelected(it.type) },
             )
             Spacer(modifier = Modifier.width(24.dp))
-            EndSidePanel(searchQuery, Modifier.fillMaxSize().padding(top = 8.dp), selectedCategory)
+            EndSidePanel(
+                searchQuery,
+                Modifier.fillMaxSize().padding(top = 8.dp),
+                selectedCategory,
+                isCustomizing = isCustomizing,
+            )
         }
     }
 }
 
 @Composable
-private fun EndSidePanel(searchQuery: String, modifier: Modifier, category: ShortcutCategoryUi?) {
+private fun CustomizeButton(onClick: () -> Unit) {
+    ShortcutHelperButton(
+        onClick = onClick,
+        color = MaterialTheme.colorScheme.secondaryContainer,
+        width = 133.dp,
+        iconSource = IconSource(imageVector = Icons.Default.Tune),
+        text = stringResource(id = R.string.shortcut_helper_customize_button_text),
+        contentColor = MaterialTheme.colorScheme.onSecondaryContainer,
+    )
+}
+
+@Composable
+private fun DoneButton(onClick: () -> Unit) {
+    ShortcutHelperButton(
+        onClick = onClick,
+        color = MaterialTheme.colorScheme.primary,
+        width = 69.dp,
+        text = stringResource(R.string.shortcut_helper_done_button_text),
+        contentColor = MaterialTheme.colorScheme.onPrimary,
+    )
+}
+
+@Composable
+private fun EndSidePanel(
+    searchQuery: String,
+    modifier: Modifier,
+    category: ShortcutCategoryUi?,
+    isCustomizing: Boolean,
+) {
     val listState = rememberLazyListState()
     LaunchedEffect(key1 = category) { if (category != null) listState.animateScrollToItem(0) }
     if (category == null) {
@@ -387,7 +443,11 @@
     }
     LazyColumn(modifier = modifier, state = listState) {
         items(category.subCategories) { subcategory ->
-            SubCategoryContainerDualPane(searchQuery = searchQuery, subCategory = subcategory)
+            SubCategoryContainerDualPane(
+                searchQuery = searchQuery,
+                subCategory = subcategory,
+                isCustomizing = isCustomizing,
+            )
             Spacer(modifier = Modifier.height(8.dp))
         }
     }
@@ -412,7 +472,11 @@
 }
 
 @Composable
-private fun SubCategoryContainerDualPane(searchQuery: String, subCategory: ShortcutSubCategory) {
+private fun SubCategoryContainerDualPane(
+    searchQuery: String,
+    subCategory: ShortcutSubCategory,
+    isCustomizing: Boolean,
+) {
     Surface(
         modifier = Modifier.fillMaxWidth(),
         shape = RoundedCornerShape(28.dp),
@@ -432,6 +496,7 @@
                     modifier = Modifier.padding(vertical = 8.dp),
                     searchQuery = searchQuery,
                     shortcut = shortcut,
+                    isCustomizing = isCustomizing,
                 )
             }
         }
@@ -448,7 +513,12 @@
 }
 
 @Composable
-private fun Shortcut(modifier: Modifier, searchQuery: String, shortcut: ShortcutModel) {
+private fun Shortcut(
+    modifier: Modifier,
+    searchQuery: String,
+    shortcut: ShortcutModel,
+    isCustomizing: Boolean = false,
+) {
     val interactionSource = remember { MutableInteractionSource() }
     val isFocused by interactionSource.collectIsFocusedAsState()
     val focusColor = MaterialTheme.colorScheme.secondary
@@ -471,7 +541,7 @@
             ShortcutDescriptionText(searchQuery = searchQuery, shortcut = shortcut)
         }
         Spacer(modifier = Modifier.width(24.dp))
-        ShortcutKeyCombinations(modifier = Modifier.weight(1f), shortcut = shortcut)
+        ShortcutKeyCombinations(modifier = Modifier.weight(1f), shortcut = shortcut, isCustomizing)
     }
 }
 
@@ -495,7 +565,11 @@
 
 @OptIn(ExperimentalLayoutApi::class)
 @Composable
-private fun ShortcutKeyCombinations(modifier: Modifier = Modifier, shortcut: ShortcutModel) {
+private fun ShortcutKeyCombinations(
+    modifier: Modifier = Modifier,
+    shortcut: ShortcutModel,
+    isCustomizing: Boolean = false,
+) {
     FlowRow(
         modifier = modifier,
         verticalArrangement = Arrangement.spacedBy(8.dp),
@@ -507,6 +581,25 @@
             }
             ShortcutCommand(command)
         }
+        if (isCustomizing) {
+            Spacer(modifier = Modifier.width(16.dp))
+            ShortcutHelperButton(
+                modifier =
+                    Modifier.border(
+                        width = 1.dp,
+                        color = MaterialTheme.colorScheme.outline,
+                        shape = CircleShape,
+                    ),
+                onClick = {},
+                color = Color.Transparent,
+                width = 32.dp,
+                height = 32.dp,
+                iconSource = IconSource(imageVector = Icons.Default.Add),
+                contentColor = MaterialTheme.colorScheme.primary,
+                contentPaddingVertical = 0.dp,
+                contentPaddingHorizontal = 0.dp,
+            )
+        }
     }
 }
 
@@ -700,12 +793,18 @@
 
 @Composable
 @OptIn(ExperimentalMaterial3Api::class)
-private fun TitleBar() {
+private fun TitleBar(isCustomizing: Boolean = false) {
+    val text =
+        if (isCustomizing) {
+            stringResource(R.string.shortcut_helper_customize_mode_title)
+        } else {
+            stringResource(R.string.shortcut_helper_title)
+        }
     CenterAlignedTopAppBar(
         colors = TopAppBarDefaults.centerAlignedTopAppBarColors(containerColor = Color.Transparent),
         title = {
             Text(
-                text = stringResource(R.string.shortcut_helper_title),
+                text = text,
                 color = MaterialTheme.colorScheme.onSurface,
                 style = MaterialTheme.typography.headlineSmall,
             )
@@ -753,14 +852,12 @@
 
 @Composable
 private fun KeyboardSettings(horizontalPadding: Dp, verticalPadding: Dp, onClick: () -> Unit) {
-    val interactionSource = remember { MutableInteractionSource() }
     ClickableShortcutSurface(
         onClick = onClick,
         shape = RoundedCornerShape(24.dp),
         color = Color.Transparent,
         modifier =
             Modifier.semantics { role = Role.Button }.fillMaxWidth().padding(horizontal = 12.dp),
-        interactionSource = interactionSource,
         interactionsConfig =
             InteractionsConfig(
                 hoverOverlayColor = MaterialTheme.colorScheme.onSurface,
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/Surfaces.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/Surfaces.kt
index f64d59a..435968e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/Surfaces.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/Surfaces.kt
@@ -27,13 +27,24 @@
 import androidx.compose.foundation.interaction.MutableInteractionSource
 import androidx.compose.foundation.interaction.PressInteraction
 import androidx.compose.foundation.interaction.collectIsFocusedAsState
+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.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.layout.wrapContentSize
 import androidx.compose.foundation.selection.selectable
+import androidx.compose.foundation.shape.RoundedCornerShape
 import androidx.compose.material3.ColorScheme
+import androidx.compose.material3.Icon
 import androidx.compose.material3.LocalAbsoluteTonalElevation
 import androidx.compose.material3.LocalContentColor
 import androidx.compose.material3.LocalTonalElevationEnabled
 import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Text
 import androidx.compose.material3.contentColorFor
 import androidx.compose.material3.minimumInteractiveComponentSize
 import androidx.compose.material3.surfaceColorAtElevation
@@ -43,6 +54,7 @@
 import androidx.compose.runtime.Stable
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
+import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.geometry.CornerRadius
 import androidx.compose.ui.geometry.Offset
@@ -57,11 +69,16 @@
 import androidx.compose.ui.node.DelegatableNode
 import androidx.compose.ui.node.DrawModifierNode
 import androidx.compose.ui.platform.LocalDensity
+import androidx.compose.ui.semantics.Role
+import androidx.compose.ui.semantics.role
+import androidx.compose.ui.semantics.semantics
 import androidx.compose.ui.unit.Dp
 import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.sp
 import androidx.compose.ui.zIndex
-import com.android.compose.modifiers.thenIf
 import com.android.app.tracing.coroutines.launchTraced as launch
+import com.android.compose.modifiers.thenIf
+import com.android.systemui.keyboard.shortcut.ui.model.IconSource
 
 /**
  * A selectable surface with no default focus/hover indications.
@@ -175,6 +192,96 @@
     }
 }
 
+/**
+ * A composable that provides a button with a customizable icon and text, designed to be re-used
+ * across shortcut helper/customizer. Supports defaults hover/focus/pressed states used across
+ * shortcut helper.
+ *
+ * This button utilizes [ClickableShortcutSurface] to provide a clickable surface with hover and
+ * pressed states, and a focus outline.
+ *
+ * The content of the button can be an icon (from [IconSource]) and/or text.
+ *
+ * @param modifier The modifier to be applied to the button.
+ * @param onClick The callback function that will be invoked when the button is clicked.
+ * @param shape The shape of the button. Defaults to a rounded corner shape used across shortcut
+ *   helper.
+ * @param color The background color of the button.
+ * @param width The width of the button.
+ * @param height The height of the button. Defaults to 40.dp as often used in shortcut helper
+ * @param iconSource The source of the icon to be displayed. Defaults to an empty [IconSource].
+ * @param text The text to be displayed. Defaults to null.
+ * @param contentColor The color of the icon and text.
+ * @param contentPaddingHorizontal The horizontal padding of the content. Defaults to 16.dp.
+ * @param contentPaddingVertical The vertical padding of the content. Defaults to 10.dp.
+ */
+@Composable
+fun ShortcutHelperButton(
+    modifier: Modifier = Modifier,
+    onClick: () -> Unit,
+    shape: Shape = RoundedCornerShape(360.dp),
+    color: Color,
+    width: Dp,
+    height: Dp = 40.dp,
+    iconSource: IconSource = IconSource(),
+    text: String? = null,
+    contentColor: Color,
+    contentPaddingHorizontal: Dp = 16.dp,
+    contentPaddingVertical: Dp = 10.dp,
+) {
+    ClickableShortcutSurface(
+        onClick = onClick,
+        shape = shape,
+        color = color,
+        modifier = modifier.semantics { role = Role.Button }.width(width).height(height),
+        interactionsConfig =
+            InteractionsConfig(
+                hoverOverlayColor = MaterialTheme.colorScheme.onSurface,
+                hoverOverlayAlpha = 0.11f,
+                pressedOverlayColor = MaterialTheme.colorScheme.onSurface,
+                pressedOverlayAlpha = 0.15f,
+                focusOutlineColor = MaterialTheme.colorScheme.secondary,
+                focusOutlineStrokeWidth = 3.dp,
+                focusOutlinePadding = 2.dp,
+                surfaceCornerRadius = 28.dp,
+                focusOutlineCornerRadius = 33.dp,
+            ),
+    ) {
+        Row(
+            modifier =
+                Modifier.padding(
+                    horizontal = contentPaddingHorizontal,
+                    vertical = contentPaddingVertical,
+                ),
+            verticalAlignment = Alignment.CenterVertically,
+            horizontalArrangement = Arrangement.Center,
+        ) {
+            if (iconSource.imageVector != null) {
+                Icon(
+                    tint = contentColor,
+                    imageVector = iconSource.imageVector,
+                    contentDescription = null,
+                    modifier = Modifier.size(20.dp).wrapContentSize(Alignment.Center),
+                )
+            }
+
+            if (iconSource.imageVector != null && text != null) {
+                Spacer(modifier = Modifier.weight(1f))
+            }
+
+            if (text != null) {
+                Text(
+                    text,
+                    color = contentColor,
+                    fontSize = 14.sp,
+                    style = MaterialTheme.typography.labelLarge,
+                    modifier = Modifier.wrapContentSize(Alignment.Center),
+                )
+            }
+        }
+    }
+}
+
 @Composable
 private fun surfaceColorAtElevation(color: Color, elevation: Dp): Color {
     return MaterialTheme.colorScheme.applyTonalElevation(color, elevation)
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/model/ShortcutsUiState.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/model/ShortcutsUiState.kt
index 8f23261..02b0b43 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/model/ShortcutsUiState.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/model/ShortcutsUiState.kt
@@ -24,6 +24,7 @@
         val searchQuery: String,
         val shortcutCategories: List<ShortcutCategoryUi>,
         val defaultSelectedCategory: ShortcutCategoryType?,
+        val isShortcutCustomizerFlagEnabled: Boolean = false,
     ) : ShortcutsUiState
 
     data object Inactive : ShortcutsUiState
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 20d09ed..912bfe9 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
@@ -26,6 +26,7 @@
 import androidx.compose.material.icons.filled.Tv
 import androidx.compose.material.icons.filled.VerticalSplit
 import com.android.compose.ui.graphics.painter.DrawablePainter
+import com.android.systemui.Flags.keyboardShortcutHelperShortcutCustomizer
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.keyboard.shortcut.domain.interactor.ShortcutHelperCategoriesInteractor
 import com.android.systemui.keyboard.shortcut.domain.interactor.ShortcutHelperStateInteractor
@@ -86,6 +87,7 @@
                         searchQuery = query,
                         shortcutCategories = shortcutCategoriesUi,
                         defaultSelectedCategory = getDefaultSelectedCategory(filteredCategories),
+                        isShortcutCustomizerFlagEnabled = keyboardShortcutHelperShortcutCustomizer(),
                     )
                 }
             }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
index 67625d0..32c2bc7 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
@@ -81,6 +81,7 @@
 import com.android.systemui.keyguard.domain.interactor.KeyguardDismissInteractor;
 import com.android.systemui.keyguard.domain.interactor.KeyguardEnabledInteractor;
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
+import com.android.systemui.keyguard.domain.interactor.KeyguardLockWhileAwakeInteractor;
 import com.android.systemui.keyguard.domain.interactor.KeyguardStateCallbackInteractor;
 import com.android.systemui.keyguard.domain.interactor.KeyguardWakeDirectlyToGoneInteractor;
 import com.android.systemui.keyguard.ui.binder.KeyguardSurfaceBehindParamsApplier;
@@ -329,6 +330,8 @@
             return new FoldGracePeriodProvider();
         }
     };
+    private final KeyguardLockWhileAwakeInteractor
+            mKeyguardLockWhileAwakeInteractor;
 
     @Inject
     public KeyguardService(
@@ -353,7 +356,8 @@
             KeyguardWakeDirectlyToGoneInteractor keyguardWakeDirectlyToGoneInteractor,
             KeyguardDismissInteractor keyguardDismissInteractor,
             Lazy<DeviceEntryInteractor> deviceEntryInteractorLazy,
-            KeyguardStateCallbackInteractor keyguardStateCallbackInteractor) {
+            KeyguardStateCallbackInteractor keyguardStateCallbackInteractor,
+            KeyguardLockWhileAwakeInteractor keyguardLockWhileAwakeInteractor) {
         super();
         mKeyguardViewMediator = keyguardViewMediator;
         mKeyguardLifecyclesDispatcher = keyguardLifecyclesDispatcher;
@@ -385,6 +389,7 @@
         mKeyguardEnabledInteractor = keyguardEnabledInteractor;
         mKeyguardWakeDirectlyToGoneInteractor = keyguardWakeDirectlyToGoneInteractor;
         mKeyguardDismissInteractor = keyguardDismissInteractor;
+        mKeyguardLockWhileAwakeInteractor = keyguardLockWhileAwakeInteractor;
     }
 
     @Override
@@ -656,6 +661,11 @@
         public void doKeyguardTimeout(Bundle options) {
             trace("doKeyguardTimeout");
             checkPermission();
+
+            if (KeyguardWmStateRefactor.isEnabled()) {
+                mKeyguardLockWhileAwakeInteractor.onKeyguardServiceDoKeyguardTimeout(options);
+            }
+
             mKeyguardViewMediator.doKeyguardTimeout(options);
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
index 654c763..3b85b57 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
@@ -319,6 +319,11 @@
     @Override
     public boolean onCreateSliceProvider() {
         mContextAvailableCallback.onContextAvailable(getContext());
+        if (mMediaManager == null) {
+            Log.e(TAG, "Dagger injection failed, cannot start. See any above warnings with string: "
+                    + "\"No injector for class\"");
+            return false;
+        }
         mMediaWakeLock = new SettableWakeLock(
                 WakeLock.createPartial(getContext(), mWakeLockLogger, "media"), "media");
         synchronized (KeyguardSliceProvider.sInstanceLock) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
index bbaa9eb..d3c17cc 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
@@ -269,6 +269,8 @@
     /** The top of shortcut in screen, used by wallpaper to find remaining space in lockscreen */
     val shortcutAbsoluteTop: StateFlow<Float>
 
+    val notificationStackAbsoluteBottom: StateFlow<Float>
+
     /**
      * Returns `true` if the keyguard is showing; `false` otherwise.
      *
@@ -339,6 +341,12 @@
     fun isShowKeyguardWhenReenabled(): Boolean
 
     fun setShortcutAbsoluteTop(top: Float)
+
+    /**
+     * Set bottom of notifications from notification stack, and Magic Portrait will layout base on
+     * this value
+     */
+    fun setNotificationStackAbsoluteBottom(bottom: Float)
 }
 
 /** Encapsulates application state for the keyguard. */
@@ -635,6 +643,9 @@
     private val _shortcutAbsoluteTop = MutableStateFlow(0F)
     override val shortcutAbsoluteTop = _shortcutAbsoluteTop.asStateFlow()
 
+    private val _notificationStackAbsoluteBottom = MutableStateFlow(0F)
+    override val notificationStackAbsoluteBottom = _notificationStackAbsoluteBottom.asStateFlow()
+
     init {
         val callback =
             object : KeyguardStateController.Callback {
@@ -717,6 +728,10 @@
         _shortcutAbsoluteTop.value = top
     }
 
+    override fun setNotificationStackAbsoluteBottom(bottom: Float) {
+        _notificationStackAbsoluteBottom.value = bottom
+    }
+
     private fun dozeMachineStateToModel(state: DozeMachine.State): DozeStateModel {
         return when (state) {
             DozeMachine.State.UNINITIALIZED -> DozeStateModel.UNINITIALIZED
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 b7d0d45..3a5614f 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
@@ -25,6 +25,7 @@
 import android.util.Log
 import com.android.app.animation.Interpolators
 import com.android.app.tracing.coroutines.withContextTraced as withContext
+import com.android.systemui.Flags.transitionRaceCondition
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.keyguard.shared.model.KeyguardState
@@ -77,6 +78,8 @@
 
     /** The [TransitionInfo] of the most recent call to [startTransition]. */
     val currentTransitionInfoInternal: StateFlow<TransitionInfo>
+    /** The [TransitionInfo] of the most recent call to [startTransition]. */
+    val currentTransitionInfo: TransitionInfo
 
     /**
      * Interactors that require information about changes between [KeyguardState]s will call this to
@@ -132,7 +135,7 @@
     private var lastStep: TransitionStep = TransitionStep()
     private var lastAnimator: ValueAnimator? = null
 
-    private val _currentTransitionMutex = Mutex()
+    private val withContextMutex = Mutex()
     private val _currentTransitionInfo: MutableStateFlow<TransitionInfo> =
         MutableStateFlow(
             TransitionInfo(
@@ -144,6 +147,16 @@
         )
     override var currentTransitionInfoInternal = _currentTransitionInfo.asStateFlow()
 
+    @Volatile
+    override var currentTransitionInfo: TransitionInfo =
+        TransitionInfo(
+            ownerName = "",
+            from = KeyguardState.OFF,
+            to = KeyguardState.OFF,
+            animator = null,
+        )
+        private set
+
     /*
      * When manual control of the transition is requested, a unique [UUID] is used as the handle
      * to permit calls to [updateTransition]
@@ -163,13 +176,17 @@
     }
 
     override suspend fun startTransition(info: TransitionInfo): UUID? {
-        _currentTransitionInfo.value = info
+        if (transitionRaceCondition()) {
+            currentTransitionInfo = info
+        } else {
+            _currentTransitionInfo.value = info
+        }
         Log.d(TAG, "(Internal) Setting current transition info: $info")
 
         // There is no fairness guarantee with 'withContext', which means that transitions could
         // be processed out of order. Use a Mutex to guarantee ordering. [updateTransition]
         // requires the same lock
-        _currentTransitionMutex.lock()
+        withContextMutex.lock()
         // Only used in a test environment
         if (forceDelayForRaceConditionTest) {
             delay(50L)
@@ -177,7 +194,7 @@
 
         // Animators must be started on the main thread.
         return withContext("$TAG#startTransition", mainDispatcher) {
-            _currentTransitionMutex.unlock()
+            withContextMutex.unlock()
             if (lastStep.from == info.from && lastStep.to == info.to) {
                 Log.i(TAG, "Duplicate call to start the transition, rejecting: $info")
                 return@withContext null
@@ -265,9 +282,9 @@
         // There is no fairness guarantee with 'withContext', which means that transitions could
         // be processed out of order. Use a Mutex to guarantee ordering. [startTransition]
         // requires the same lock
-        _currentTransitionMutex.lock()
+        withContextMutex.lock()
         withContext("$TAG#updateTransition", mainDispatcher) {
-            _currentTransitionMutex.unlock()
+            withContextMutex.unlock()
 
             updateTransitionInternal(transitionId, value, state)
         }
@@ -302,13 +319,23 @@
         // Tests runs on testDispatcher, which is not the main thread, causing the animator thread
         // check to fail
         if (testSetup) {
-            _currentTransitionInfo.value =
-                TransitionInfo(
-                    ownerName = ownerName,
-                    from = KeyguardState.OFF,
-                    to = to,
-                    animator = null,
-                )
+            if (transitionRaceCondition()) {
+                currentTransitionInfo =
+                    TransitionInfo(
+                        ownerName = ownerName,
+                        from = KeyguardState.OFF,
+                        to = to,
+                        animator = null,
+                    )
+            } else {
+                _currentTransitionInfo.value =
+                    TransitionInfo(
+                        ownerName = ownerName,
+                        from = KeyguardState.OFF,
+                        to = to,
+                        animator = null,
+                    )
+            }
             emitTransition(
                 TransitionStep(
                     KeyguardState.OFF,
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 bc2ed71..bd9836f 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
@@ -24,8 +24,6 @@
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.keyguard.KeyguardWmStateRefactor
-import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository
-import com.android.systemui.keyguard.data.repository.KeyguardRepository
 import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.keyguard.shared.model.TransitionModeOnCanceled
@@ -54,9 +52,7 @@
     powerInteractor: PowerInteractor,
     private val communalSceneInteractor: CommunalSceneInteractor,
     keyguardOcclusionInteractor: KeyguardOcclusionInteractor,
-    private val biometricSettingsRepository: BiometricSettingsRepository,
-    private val keyguardRepository: KeyguardRepository,
-    private val keyguardEnabledInteractor: KeyguardEnabledInteractor,
+    private val keyguardLockWhileAwakeInteractor: KeyguardLockWhileAwakeInteractor,
 ) :
     TransitionInteractor(
         fromState = KeyguardState.GONE,
@@ -78,7 +74,9 @@
     }
 
     fun showKeyguard() {
-        scope.launch("$TAG#showKeyguard") { startTransitionTo(KeyguardState.LOCKSCREEN) }
+        scope.launch("$TAG#showKeyguard") {
+            startTransitionTo(KeyguardState.LOCKSCREEN, ownerReason = "showKeyguard()")
+        }
     }
 
     /**
@@ -102,34 +100,18 @@
     // Primarily for when the user chooses to lock down the device
     private fun listenForGoneToLockscreenOrHubOrOccluded() {
         if (KeyguardWmStateRefactor.isEnabled) {
-            scope.launch("$TAG#listenForGoneToLockscreenOrHub") {
-                biometricSettingsRepository.isCurrentUserInLockdown
-                    .distinctUntilChanged()
-                    .filterRelevantKeyguardStateAnd { inLockdown -> inLockdown }
+            scope.launch {
+                keyguardLockWhileAwakeInteractor.lockWhileAwakeEvents
+                    .filterRelevantKeyguardState()
                     .sample(communalSceneInteractor.isIdleOnCommunalNotEditMode, ::Pair)
-                    .collect { (_, isIdleOnCommunal) ->
+                    .collect { (lockReason, idleOnCommunal) ->
                         val to =
-                            if (isIdleOnCommunal) {
+                            if (idleOnCommunal) {
                                 KeyguardState.GLANCEABLE_HUB
                             } else {
                                 KeyguardState.LOCKSCREEN
                             }
-                        startTransitionTo(to, ownerReason = "User initiated lockdown")
-                    }
-            }
-
-            scope.launch {
-                keyguardRepository.isKeyguardEnabled
-                    .filterRelevantKeyguardStateAnd { enabled -> enabled }
-                    .sample(keyguardEnabledInteractor.showKeyguardWhenReenabled)
-                    .filter { reshow -> reshow }
-                    .collect {
-                        startTransitionTo(
-                            KeyguardState.LOCKSCREEN,
-                            ownerReason =
-                                "Keyguard was re-enabled, and we weren't GONE when it " +
-                                    "was originally disabled",
-                        )
+                        startTransitionTo(to, ownerReason = "lockWhileAwake: $lockReason")
                     }
             }
         } else {
@@ -146,7 +128,10 @@
                             } else {
                                 KeyguardState.LOCKSCREEN
                             }
-                        startTransitionTo(to)
+                        startTransitionTo(
+                            to,
+                            ownerReason = "keyguard interactor says keyguard is showing",
+                        )
                     }
             }
         }
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 a7dde34e..b60e98a 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
@@ -40,6 +40,7 @@
 import com.android.systemui.scene.shared.model.Scenes
 import com.android.systemui.shade.data.repository.ShadeRepository
 import com.android.systemui.util.kotlin.Utils.Companion.sample as sampleCombine
+import com.android.systemui.util.kotlin.sample
 import java.util.UUID
 import javax.inject.Inject
 import kotlin.time.Duration.Companion.milliseconds
@@ -132,11 +133,10 @@
         scope.launch("$TAG#listenForLockscreenToDreaming") {
             keyguardInteractor.isAbleToDream
                 .filterRelevantKeyguardState()
-                .sampleCombine(
-                    internalTransitionInteractor.currentTransitionInfoInternal,
-                    transitionInteractor.isFinishedIn(KeyguardState.LOCKSCREEN),
-                )
-                .collect { (isAbleToDream, transitionInfo, isOnLockscreen) ->
+                .sample(transitionInteractor.isFinishedIn(KeyguardState.LOCKSCREEN), ::Pair)
+                .collect { (isAbleToDream, isOnLockscreen) ->
+                    val transitionInfo =
+                        internalTransitionInteractor.currentTransitionInfoInternal()
                     val isTransitionInterruptible =
                         transitionInfo.to == KeyguardState.LOCKSCREEN &&
                             !invalidFromStates.contains(transitionInfo.from)
@@ -179,7 +179,6 @@
             shadeRepository.legacyShadeExpansion
                 .sampleCombine(
                     transitionInteractor.startedKeyguardTransitionStep,
-                    internalTransitionInteractor.currentTransitionInfoInternal,
                     keyguardInteractor.statusBarState,
                     keyguardInteractor.isKeyguardDismissible,
                     keyguardInteractor.isKeyguardOccluded,
@@ -188,11 +187,12 @@
                     (
                         shadeExpansion,
                         startedStep,
-                        currentTransitionInfo,
                         statusBarState,
                         isKeyguardUnlocked,
                         isKeyguardOccluded) ->
                     val id = transitionId
+                    val currentTransitionInfo =
+                        internalTransitionInteractor.currentTransitionInfoInternal()
                     if (id != null) {
                         if (startedStep.to == KeyguardState.PRIMARY_BOUNCER) {
                             // An existing `id` means a transition is started, and calls to
@@ -288,6 +288,7 @@
                     startTransitionTo(
                         KeyguardState.GONE,
                         modeOnCanceled = TransitionModeOnCanceled.RESET,
+                        ownerReason = "keyguard interactor says keyguard is going away",
                     )
                 }
         }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/InternalKeyguardTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/InternalKeyguardTransitionInteractor.kt
index 2cc6afa..0507834 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/InternalKeyguardTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/InternalKeyguardTransitionInteractor.kt
@@ -17,13 +17,13 @@
 package com.android.systemui.keyguard.domain.interactor
 
 import android.annotation.FloatRange
+import com.android.systemui.Flags.transitionRaceCondition
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
 import com.android.systemui.keyguard.shared.model.TransitionInfo
 import com.android.systemui.keyguard.shared.model.TransitionState
 import java.util.UUID
 import javax.inject.Inject
-import kotlinx.coroutines.flow.StateFlow
 
 /**
  * This interactor provides direct access to [KeyguardTransitionRepository] internals and exposes
@@ -32,9 +32,7 @@
 @SysUISingleton
 class InternalKeyguardTransitionInteractor
 @Inject
-constructor(
-    private val repository: KeyguardTransitionRepository,
-) {
+constructor(private val repository: KeyguardTransitionRepository) {
 
     /**
      * The [TransitionInfo] of the most recent call to
@@ -58,14 +56,19 @@
      * *will* be emitted, and therefore that it can safely request an AOD -> LOCKSCREEN transition
      * which will subsequently cancel GONE -> AOD.
      */
-    internal val currentTransitionInfoInternal: StateFlow<TransitionInfo> =
-        repository.currentTransitionInfoInternal
+    internal fun currentTransitionInfoInternal(): TransitionInfo {
+        return if (transitionRaceCondition()) {
+            repository.currentTransitionInfo
+        } else {
+            repository.currentTransitionInfoInternal.value
+        }
+    }
 
     suspend fun startTransition(info: TransitionInfo) = repository.startTransition(info)
 
     suspend fun updateTransition(
         transitionId: UUID,
         @FloatRange(from = 0.0, to = 1.0) value: Float,
-        state: TransitionState
+        state: TransitionState,
     ) = repository.updateTransition(transitionId, value, state)
 }
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 258232b..21090c1 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
@@ -17,7 +17,9 @@
 
 package com.android.systemui.keyguard.domain.interactor
 
+import com.android.keyguard.logging.KeyguardLogger
 import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor
+import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
 import com.android.systemui.bouncer.shared.flag.ComposeBouncerFlags
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
@@ -27,28 +29,31 @@
 import com.android.systemui.keyguard.shared.model.KeyguardDone
 import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
 import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER
+import com.android.systemui.lifecycle.ExclusiveActivatable
+import com.android.systemui.log.core.LogLevel
 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.shade.domain.interactor.ShadeInteractor
 import com.android.systemui.util.kotlin.Utils.Companion.sampleFilter
-import com.android.systemui.util.kotlin.sample
 import dagger.Lazy
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.awaitCancellation
+import kotlinx.coroutines.coroutineScope
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.distinctUntilChanged
 import kotlinx.coroutines.flow.filter
-import kotlinx.coroutines.flow.filterNot
 import kotlinx.coroutines.flow.flow
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.merge
 import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.launch
 
 /** Encapsulates business-logic for actions to run when the keyguard is dismissed. */
 @ExperimentalCoroutinesApi
@@ -66,10 +71,10 @@
     shadeInteractor: Lazy<ShadeInteractor>,
     keyguardInteractor: Lazy<KeyguardInteractor>,
     sceneInteractor: Lazy<SceneInteractor>,
-) {
-    val dismissAction: Flow<DismissAction> = repository.dismissAction
-
-    val onCancel: Flow<Runnable> = dismissAction.map { it.onCancelAction }
+    private val keyguardLogger: KeyguardLogger,
+    private val primaryBouncerInteractor: PrimaryBouncerInteractor,
+) : ExclusiveActivatable() {
+    private val dismissAction: Flow<DismissAction> = repository.dismissAction
 
     // TODO (b/268240415): use message in alt + primary bouncer message
     // message to show to the user about the dismiss action, else empty string
@@ -90,10 +95,24 @@
             )
 
     private val finishedTransitionToGone: Flow<Unit> =
-        transitionInteractor
-            .isFinishedIn(scene = Scenes.Gone, stateWithoutSceneContainer = GONE)
-            .filter { it }
-            .map {}
+        if (SceneContainerFlag.isEnabled) {
+            // Using sceneInteractor instead of transitionInteractor because of a race
+            // condition that forms between transitionInteractor (transitionState) and
+            // isOnShadeWhileUnlocked where the latter emits false before the former emits
+            // true, causing the merge to not emit until it's too late.
+            sceneInteractor
+                .get()
+                .currentScene
+                .map { it == Scenes.Gone }
+                .distinctUntilChanged()
+                .filter { it }
+                .map {}
+        } else {
+            transitionInteractor
+                .isFinishedIn(scene = Scenes.Gone, stateWithoutSceneContainer = GONE)
+                .filter { it }
+                .map {}
+        }
 
     /**
      * True if the any variation of the notification shade or quick settings is showing AND the
@@ -125,30 +144,8 @@
             }
         }
 
-    val executeDismissAction: Flow<() -> KeyguardDone> =
-        merge(
-                if (SceneContainerFlag.isEnabled) {
-                    // Using currentScene instead of finishedTransitionToGone because of a race
-                    // condition that forms between finishedTransitionToGone and
-                    // isOnShadeWhileUnlocked where the latter emits false before the former emits
-                    // true, causing the merge to not emit until it's too late.
-                    sceneInteractor
-                        .get()
-                        .currentScene
-                        .map { it == Scenes.Gone }
-                        .distinctUntilChanged()
-                        .filter { it }
-                } else {
-                    finishedTransitionToGone
-                },
-                isOnShadeWhileUnlocked.filter { it }.map {},
-                dismissInteractor.dismissKeyguardRequestWithImmediateDismissAction,
-            )
-            .sample(dismissAction)
-            .filterNot { it is DismissAction.None }
-            .map { it.onDismissAction }
-
-    val resetDismissAction: Flow<Unit> =
+    /** Flow that emits whenever we need to reset the dismiss action */
+    private val resetDismissAction: Flow<Unit> =
         combine(
                 if (SceneContainerFlag.isEnabled) {
                     // Using currentScene instead of isFinishedIn because of a race condition that
@@ -205,13 +202,62 @@
         repository.setDismissAction(dismissAction)
     }
 
-    fun handleDismissAction() {
-        if (ComposeBouncerFlags.isUnexpectedlyInLegacyMode()) return
+    /** Launch any relevant coroutines that are required by this interactor. */
+    override suspend fun onActivated(): Nothing {
+        coroutineScope {
+            launch {
+                merge(finishedTransitionToGone, isOnShadeWhileUnlocked.filter { it }.map {})
+                    .collect {
+                        log("finishedTransitionToGone")
+                        runDismissAction()
+                    }
+            }
+
+            launch {
+                dismissInteractor.dismissKeyguardRequestWithImmediateDismissAction.collect {
+                    log("eventsThatRequireKeyguardDismissal")
+                    runDismissAction()
+                }
+            }
+
+            launch {
+                resetDismissAction.collect {
+                    log("resetDismissAction")
+                    repository.dismissAction.value.onCancelAction.run()
+                    clearDismissAction()
+                }
+            }
+
+            launch { repository.dismissAction.collect { log("updatedDismissAction=$it") } }
+            awaitCancellation()
+        }
+    }
+
+    /** Run the dismiss action and starts the dismiss keyguard transition. */
+    private suspend fun runDismissAction() {
+        val dismissAction = repository.dismissAction.value
+        var keyguardDoneTiming: KeyguardDone = KeyguardDone.IMMEDIATE
+        if (dismissAction != DismissAction.None) {
+            keyguardDoneTiming = dismissAction.onDismissAction.invoke()
+            dismissInteractor.setKeyguardDone(keyguardDoneTiming)
+            clearDismissAction()
+        }
+        if (!SceneContainerFlag.isEnabled) {
+            // This is required to reset some state flows in the repository which ideally should be
+            // sharedFlows but are not due to performance concerns.
+            primaryBouncerInteractor.notifyKeyguardAuthenticatedHandled()
+        }
+    }
+
+    private fun clearDismissAction() {
         repository.setDismissAction(DismissAction.None)
     }
 
-    suspend fun setKeyguardDone(keyguardDoneTiming: KeyguardDone) {
-        if (ComposeBouncerFlags.isUnexpectedlyInLegacyMode()) return
-        dismissInteractor.setKeyguardDone(keyguardDoneTiming)
+    private fun log(message: String) {
+        keyguardLogger.log(TAG, LogLevel.DEBUG, message)
+    }
+
+    companion object {
+        private const val TAG = "KeyguardDismissAction"
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissTransitionInteractor.kt
index c19bbbc..4793d95 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissTransitionInteractor.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.keyguard.domain.interactor
 
 import android.util.Log
+import com.android.systemui.Flags.transitionRaceCondition
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
 import com.android.systemui.keyguard.shared.model.KeyguardState
@@ -51,7 +52,13 @@
     fun startDismissKeyguardTransition(reason: String = "") {
         if (SceneContainerFlag.isEnabled) return
         Log.d(TAG, "#startDismissKeyguardTransition(reason=$reason)")
-        when (val startedState = repository.currentTransitionInfoInternal.value.to) {
+        val startedState =
+            if (transitionRaceCondition()) {
+                repository.currentTransitionInfo.to
+            } else {
+                repository.currentTransitionInfoInternal.value.to
+            }
+        when (startedState) {
             LOCKSCREEN -> fromLockscreenTransitionInteractor.dismissKeyguard()
             PRIMARY_BOUNCER -> fromPrimaryBouncerTransitionInteractor.dismissPrimaryBouncer()
             ALTERNATE_BOUNCER -> fromAlternateBouncerTransitionInteractor.dismissAlternateBouncer()
@@ -61,7 +68,7 @@
             KeyguardState.GONE ->
                 Log.i(
                     TAG,
-                    "Already transitioning to GONE; ignoring startDismissKeyguardTransition."
+                    "Already transitioning to GONE; ignoring startDismissKeyguardTransition.",
                 )
             else -> Log.e(TAG, "We don't know how to dismiss keyguard from state $startedState.")
         }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardEnabledInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardEnabledInteractor.kt
index 5f08aa3..631e44a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardEnabledInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardEnabledInteractor.kt
@@ -22,7 +22,7 @@
 import com.android.systemui.keyguard.data.repository.KeyguardRepository
 import com.android.systemui.keyguard.shared.model.KeyguardState
 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
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.flow.Flow
@@ -74,11 +74,9 @@
             .onEach { SceneContainerFlag.assertInLegacyMode() }
             // Whenever the keyguard is disabled...
             .filter { enabled -> !enabled }
-            .sampleCombine(
-                internalTransitionInteractor.currentTransitionInfoInternal,
-                biometricSettingsRepository.isCurrentUserInLockdown,
-            )
-            .map { (_, transitionInfo, inLockdown) ->
+            .sample(biometricSettingsRepository.isCurrentUserInLockdown, ::Pair)
+            .map { (_, inLockdown) ->
+                val transitionInfo = internalTransitionInteractor.currentTransitionInfoInternal()
                 // ...we hide the keyguard, if it's showing and we're not in lockdown. In that case,
                 // we want to remember that and re-show it when keyguard is enabled again.
                 transitionInfo.to != KeyguardState.GONE && !inLockdown
@@ -93,11 +91,10 @@
             if (!SceneContainerFlag.isEnabled) {
                 repository.isKeyguardEnabled
                     .filter { enabled -> !enabled }
-                    .sampleCombine(
-                        biometricSettingsRepository.isCurrentUserInLockdown,
-                        internalTransitionInteractor.currentTransitionInfoInternal,
-                    )
-                    .collect { (_, inLockdown, currentTransitionInfo) ->
+                    .sample(biometricSettingsRepository.isCurrentUserInLockdown, ::Pair)
+                    .collect { (_, inLockdown) ->
+                        val currentTransitionInfo =
+                            internalTransitionInteractor.currentTransitionInfoInternal()
                         if (currentTransitionInfo.to != KeyguardState.GONE && !inLockdown) {
                             keyguardDismissTransitionInteractor.startDismissKeyguardTransition(
                                 "keyguard disabled"
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 29c6d5a..b24ca1a 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
@@ -545,6 +545,10 @@
         repository.isKeyguardGoingAway.value = isGoingAway
     }
 
+    fun setNotificationStackAbsoluteBottom(bottom: Float) {
+        repository.setNotificationStackAbsoluteBottom(bottom)
+    }
+
     companion object {
         private const val TAG = "KeyguardInteractor"
     }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardLockWhileAwakeInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardLockWhileAwakeInteractor.kt
new file mode 100644
index 0000000..0ab3e5c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardLockWhileAwakeInteractor.kt
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.domain.interactor
+
+import android.os.Bundle
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository
+import com.android.systemui.util.kotlin.sample
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.filterNotNull
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.merge
+
+/**
+ * Emitted when we receive a [KeyguardLockWhileAwakeInteractor.onKeyguardServiceDoKeyguardTimeout]
+ * call.
+ *
+ * Includes a timestamp so it's not conflated by the StateFlow.
+ */
+data class KeyguardTimeoutWhileAwakeEvent(val timestamp: Long, val options: Bundle?)
+
+/** The reason we're locking while awake, used for logging. */
+enum class LockWhileAwakeReason(private val logReason: String) {
+    LOCKDOWN("Lockdown initiated."),
+    KEYGUARD_REENABLED(
+        "Keyguard was re-enabled. We weren't unlocked when it was disabled, " +
+            "so we're returning to the lockscreen."
+    ),
+    KEYGUARD_TIMEOUT_WHILE_SCREEN_ON(
+        "Timed out while the screen was kept on, or WM#lockNow() was called."
+    );
+
+    override fun toString(): String {
+        return logReason
+    }
+}
+
+/**
+ * Logic around cases where the device locks while still awake (transitioning from GONE ->
+ * LOCKSCREEN), vs. the more common cases of a power button press or screen timeout, which result in
+ * the device going to sleep.
+ *
+ * This is possible in the following situations:
+ * - The user initiates lockdown from the power menu.
+ * - Theft detection, etc. has requested lockdown.
+ * - The keyguard was disabled while visible, and has now been re-enabled, so it's re-showing.
+ * - Someone called WM#lockNow().
+ * - The screen timed out, but an activity with FLAG_ALLOW_LOCK_WHILE_SCREEN_ON is on top.
+ */
+@SysUISingleton
+class KeyguardLockWhileAwakeInteractor
+@Inject
+constructor(
+    biometricSettingsRepository: BiometricSettingsRepository,
+    keyguardEnabledInteractor: KeyguardEnabledInteractor,
+) {
+    /** Emits whenever a timeout event is received by [KeyguardService]. */
+    private val timeoutEvents: MutableStateFlow<KeyguardTimeoutWhileAwakeEvent?> =
+        MutableStateFlow(null)
+
+    /** Emits whenever the current user is in lockdown mode. */
+    private val inLockdown: Flow<LockWhileAwakeReason> =
+        biometricSettingsRepository.isCurrentUserInLockdown
+            .distinctUntilChanged()
+            .filter { inLockdown -> inLockdown }
+            .map { LockWhileAwakeReason.LOCKDOWN }
+
+    /**
+     * Emits whenever the keyguard is re-enabled, and we need to return to lockscreen due to the
+     * device being locked when the keyguard was originally disabled.
+     */
+    private val keyguardReenabled: Flow<LockWhileAwakeReason> =
+        keyguardEnabledInteractor.isKeyguardEnabled
+            .filter { enabled -> enabled }
+            .sample(keyguardEnabledInteractor.showKeyguardWhenReenabled)
+            .filter { reshow -> reshow }
+            .map { LockWhileAwakeReason.KEYGUARD_REENABLED }
+
+    /** Emits whenever we should lock while the screen is on, for any reason. */
+    val lockWhileAwakeEvents: Flow<LockWhileAwakeReason> =
+        merge(
+            inLockdown,
+            keyguardReenabled,
+            timeoutEvents.filterNotNull().map {
+                LockWhileAwakeReason.KEYGUARD_TIMEOUT_WHILE_SCREEN_ON
+            },
+        )
+
+    /**
+     * Called by [KeyguardService] when it receives a doKeyguardTimeout() call. This indicates that
+     * the device locked while the screen was on.
+     *
+     * [options] appears to be no longer used, but we'll keep it in this interactor in case that
+     * turns out not to be true.
+     */
+    fun onKeyguardServiceDoKeyguardTimeout(options: Bundle?) {
+        timeoutEvents.value =
+            KeyguardTimeoutWhileAwakeEvent(
+                timestamp = System.currentTimeMillis(),
+                options = options,
+            )
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardOcclusionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardOcclusionInteractor.kt
index 7f1e881..278a98f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardOcclusionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardOcclusionInteractor.kt
@@ -80,7 +80,7 @@
         // *_BOUNCER -> LOCKSCREEN.
         return powerInteractor.detailedWakefulness.value.powerButtonLaunchGestureTriggered &&
             KeyguardState.deviceIsAsleepInState(
-                internalTransitionInteractor.currentTransitionInfoInternal.value.to
+                internalTransitionInteractor.currentTransitionInfoInternal().to
             )
     }
 
@@ -100,13 +100,13 @@
                             scene = Scenes.Gone,
                             stateWithoutSceneContainer = KeyguardState.GONE,
                         ),
-                        ::Pair
+                        ::Pair,
                     )
                     .map { (wakefulness, isOnGone) ->
                         wakefulness.powerButtonLaunchGestureTriggered && !isOnGone
                     },
                 // Emit false once that activity goes away.
-                isShowWhenLockedActivityOnTop.filter { !it }.map { false }
+                isShowWhenLockedActivityOnTop.filter { !it }.map { false },
             )
             .stateIn(applicationScope, SharingStarted.Eagerly, false)
 
@@ -134,7 +134,7 @@
      */
     fun setWmNotifiedShowWhenLockedActivityOnTop(
         showWhenLockedActivityOnTop: Boolean,
-        taskInfo: RunningTaskInfo? = null
+        taskInfo: RunningTaskInfo? = null,
     ) {
         repository.setShowWhenLockedActivityInfo(showWhenLockedActivityOnTop, taskInfo)
     }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionBootInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionBootInteractor.kt
index cddeaaf..b986d56 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionBootInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionBootInteractor.kt
@@ -61,7 +61,7 @@
 
     fun start() {
         scope.launch {
-            if (internalTransitionInteractor.currentTransitionInfoInternal.value.from != OFF) {
+            if (internalTransitionInteractor.currentTransitionInfoInternal().from != OFF) {
                 Log.e(
                     "KeyguardTransitionInteractor",
                     "showLockscreenOnBoot emitted, but we've already " +
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt
index 249982d..abd7f90 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt
@@ -71,14 +71,14 @@
         ownerReason: String = "",
     ): UUID? {
         toState.checkValidState()
-        if (fromState != internalTransitionInteractor.currentTransitionInfoInternal.value.to) {
+        if (fromState != internalTransitionInteractor.currentTransitionInfoInternal().to) {
             Log.e(
                 name,
                 "Ignoring startTransition: This interactor asked to transition from " +
                     "$fromState -> $toState, but we last transitioned to " +
-                    "${internalTransitionInteractor.currentTransitionInfoInternal.value.to}, not" +
+                    "${internalTransitionInteractor.currentTransitionInfoInternal().to}, not" +
                     " $fromState. This should never happen - check currentTransitionInfoInternal" +
-                    " or use filterRelevantKeyguardState before starting transitions."
+                    " or use filterRelevantKeyguardState before starting transitions.",
             )
             return null
         }
@@ -149,7 +149,7 @@
             if (keyguardInteractor.isKeyguardDismissible.value) {
                 startTransitionTo(
                     KeyguardState.GONE,
-                    ownerReason = "Power button gesture while keyguard is dismissible"
+                    ownerReason = "Power button gesture while keyguard is dismissible",
                 )
 
                 return true
@@ -159,7 +159,7 @@
                 // should transition to GONE.
                 startTransitionTo(
                     KeyguardState.GONE,
-                    ownerReason = "Power button gesture on dismissable keyguard"
+                    ownerReason = "Power button gesture on dismissable keyguard",
                 )
 
                 return true
@@ -190,16 +190,13 @@
                 startTransitionTo(
                     toState = keyguardInteractor.asleepKeyguardState.value,
                     modeOnCanceled = modeOnCanceled,
-                    ownerReason = "Sleep transition triggered"
+                    ownerReason = "Sleep transition triggered",
                 )
             }
     }
 
     /** This signal may come in before the occlusion signal, and can provide a custom transition */
-    fun listenForTransitionToCamera(
-        scope: CoroutineScope,
-        keyguardInteractor: KeyguardInteractor,
-    ) {
+    fun listenForTransitionToCamera(scope: CoroutineScope, keyguardInteractor: KeyguardInteractor) {
         if (!KeyguardWmStateRefactor.isEnabled) {
             scope.launch {
                 keyguardInteractor.onCameraLaunchDetected.filterRelevantKeyguardState().collect {
@@ -223,7 +220,7 @@
      * [startedKeyguardState] as it does not wait for the emission of the first STARTED step.
      */
     fun inOrTransitioningToRelevantKeyguardState(): Boolean {
-        return internalTransitionInteractor.currentTransitionInfoInternal.value.to == fromState
+        return internalTransitionInteractor.currentTransitionInfoInternal().to == fromState
     }
 
     /**
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 a09cd7c..a1f6067 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
@@ -19,6 +19,7 @@
 package com.android.systemui.keyguard.domain.interactor
 
 import com.android.compose.animation.scene.ObservableTransitionState
+import com.android.systemui.Flags.transitionRaceCondition
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor
 import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
@@ -65,7 +66,7 @@
         combine(
             transitionInteractor.isFinishedIn(
                 scene = Scenes.Gone,
-                stateWithoutSceneContainer = KeyguardState.GONE
+                stateWithoutSceneContainer = KeyguardState.GONE,
             ),
             wakeToGoneInteractor.canWakeDirectlyToGone,
         ) { isOnGone, canWakeDirectlyToGone ->
@@ -197,11 +198,11 @@
             combine(
                     transitionInteractor.isInTransition(
                         edge = Edge.create(to = Scenes.Gone),
-                        edgeWithoutSceneContainer = Edge.create(to = KeyguardState.GONE)
+                        edgeWithoutSceneContainer = Edge.create(to = KeyguardState.GONE),
                     ),
                     transitionInteractor.isFinishedIn(
                         scene = Scenes.Gone,
-                        stateWithoutSceneContainer = KeyguardState.GONE
+                        stateWithoutSceneContainer = KeyguardState.GONE,
                     ),
                     surfaceBehindInteractor.isAnimatingSurface,
                     notificationLaunchAnimationInteractor.isLaunchAnimationRunning,
@@ -231,7 +232,7 @@
             combine(
                     transitionInteractor.currentKeyguardState,
                     wakeToGoneInteractor.canWakeDirectlyToGone,
-                    ::Pair
+                    ::Pair,
                 )
                 .sample(transitionInteractor.startedStepWithPrecedingStep, ::toTriple)
                 .map { (currentState, canWakeDirectlyToGone, startedWithPrev) ->
@@ -242,7 +243,12 @@
                             startedFromStep.transitionState == TransitionState.CANCELED &&
                             startedFromStep.from == KeyguardState.GONE
 
-                    val transitionInfo = transitionRepository.currentTransitionInfoInternal.value
+                    val transitionInfo =
+                        if (transitionRaceCondition()) {
+                            transitionRepository.currentTransitionInfo
+                        } else {
+                            transitionRepository.currentTransitionInfoInternal.value
+                        }
                     val wakingDirectlyToGone =
                         deviceIsAsleepInState(transitionInfo.from) &&
                             transitionInfo.to == KeyguardState.GONE
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
index 5524b20..aa44b6d 100644
--- 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
@@ -106,7 +106,7 @@
 
     private suspend fun handleIdle(
         prevTransition: ObservableTransitionState,
-        idle: ObservableTransitionState.Idle
+        idle: ObservableTransitionState.Idle,
     ) {
         if (currentTransitionId == null) return
         if (prevTransition !is ObservableTransitionState.Transition) return
@@ -133,10 +133,10 @@
         val newTransition =
             TransitionInfo(
                 ownerName = this::class.java.simpleName,
-                from = internalTransitionInteractor.currentTransitionInfoInternal.value.to,
+                from = internalTransitionInteractor.currentTransitionInfoInternal().to,
                 to = state,
                 animator = null,
-                modeOnCanceled = TransitionModeOnCanceled.REVERSE
+                modeOnCanceled = TransitionModeOnCanceled.REVERSE,
             )
         currentTransitionId = internalTransitionInteractor.startTransition(newTransition)
         internalTransitionInteractor.updateTransition(currentTransitionId!!, 1f, FINISHED)
@@ -152,8 +152,7 @@
     private suspend fun handleTransition(transition: ObservableTransitionState.Transition) {
         if (transition.fromContent == Scenes.Lockscreen) {
             if (currentTransitionId != null) {
-                val currentToState =
-                    internalTransitionInteractor.currentTransitionInfoInternal.value.to
+                val currentToState = internalTransitionInteractor.currentTransitionInfoInternal().to
                 if (currentToState == UNDEFINED) {
                     transitionKtfTo(transitionInteractor.startedKeyguardTransitionStep.value.from)
                 }
@@ -197,21 +196,21 @@
                 from = UNDEFINED,
                 to = repository.nextLockscreenTargetState.value,
                 animator = null,
-                modeOnCanceled = TransitionModeOnCanceled.RESET
+                modeOnCanceled = TransitionModeOnCanceled.RESET,
             )
         repository.nextLockscreenTargetState.value = DEFAULT_STATE
         startTransition(newTransition)
     }
 
     private suspend fun startTransitionFromLockscreen() {
-        val currentState = internalTransitionInteractor.currentTransitionInfoInternal.value.to
+        val currentState = internalTransitionInteractor.currentTransitionInfoInternal().to
         val newTransition =
             TransitionInfo(
                 ownerName = this::class.java.simpleName,
                 from = currentState,
                 to = UNDEFINED,
                 animator = null,
-                modeOnCanceled = TransitionModeOnCanceled.RESET
+                modeOnCanceled = TransitionModeOnCanceled.RESET,
             )
         startTransition(newTransition)
     }
@@ -228,7 +227,7 @@
         internalTransitionInteractor.updateTransition(
             currentTransitionId!!,
             progress.coerceIn(0f, 1f),
-            RUNNING
+            RUNNING,
         )
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardDismissActionBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardDismissActionBinder.kt
index 87befc0..f1a316c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardDismissActionBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardDismissActionBinder.kt
@@ -15,14 +15,11 @@
  */
 package com.android.systemui.keyguard.ui.binder
 
-import com.android.keyguard.logging.KeyguardLogger
 import com.android.systemui.CoreStartable
 import com.android.systemui.bouncer.shared.flag.ComposeBouncerFlags
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.keyguard.domain.interactor.KeyguardDismissActionInteractor
-import com.android.systemui.log.core.LogLevel
-import com.android.systemui.util.kotlin.sample
 import dagger.Lazy
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
@@ -37,7 +34,6 @@
 constructor(
     private val interactorLazy: Lazy<KeyguardDismissActionInteractor>,
     @Application private val scope: CoroutineScope,
-    private val keyguardLogger: KeyguardLogger,
 ) : CoreStartable {
 
     override fun start() {
@@ -45,31 +41,6 @@
             return
         }
 
-        val interactor = interactorLazy.get()
-        scope.launch {
-            interactor.executeDismissAction.collect {
-                log("executeDismissAction")
-                interactor.setKeyguardDone(it())
-                interactor.handleDismissAction()
-            }
-        }
-
-        scope.launch {
-            interactor.resetDismissAction.sample(interactor.onCancel).collect {
-                log("resetDismissAction")
-                it.run()
-                interactor.handleDismissAction()
-            }
-        }
-
-        scope.launch { interactor.dismissAction.collect { log("updatedDismissAction=$it") } }
-    }
-
-    private fun log(message: String) {
-        keyguardLogger.log(TAG, LogLevel.DEBUG, message)
-    }
-
-    companion object {
-        private const val TAG = "KeyguardDismissAction"
+        scope.launch { interactorLazy.get().activate() }
     }
 }
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 12bcc7e..b15cacf 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
@@ -35,9 +35,7 @@
 @SysUISingleton
 class DozingToOccludedTransitionViewModel
 @Inject
-constructor(
-    animationFlow: KeyguardTransitionAnimationFlow,
-) : DeviceEntryIconTransition {
+constructor(animationFlow: KeyguardTransitionAnimationFlow) : DeviceEntryIconTransition {
     private val transitionAnimation =
         animationFlow.setup(
             duration = FromAodTransitionInteractor.TO_OCCLUDED_DURATION,
@@ -56,11 +54,7 @@
         var currentAlpha = 0f
         return transitionAnimation.sharedFlow(
             duration = 250.milliseconds,
-            startTime = if (lightRevealMigration()) {
-                100.milliseconds // Wait for the light reveal to "hit" the LS elements.
-            } else {
-                0.milliseconds
-            },
+            startTime = 0.milliseconds,
             onStart = {
                 if (lightRevealMigration()) {
                     currentAlpha = viewState.alpha()
@@ -69,7 +63,6 @@
                 }
             },
             onStep = { MathUtils.lerp(currentAlpha, 0f, it) },
-            onCancel = { 0f },
         )
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaProcessingHelper.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaProcessingHelper.kt
index 55d7b1d..2bdee67 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaProcessingHelper.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaProcessingHelper.kt
@@ -42,7 +42,7 @@
     context: Context,
     newController: MediaController,
     new: MediaData,
-    old: MediaData?
+    old: MediaData?,
 ): Boolean {
     if (old == null || !mediaControlsPostsOptimization()) return false
 
@@ -71,7 +71,7 @@
 /** Returns whether actions lists are equal. */
 fun areCustomActionListsEqual(
     first: List<PlaybackState.CustomAction>?,
-    second: List<PlaybackState.CustomAction>?
+    second: List<PlaybackState.CustomAction>?,
 ): Boolean {
     // Same object, or both null
     if (first === second) {
@@ -94,7 +94,7 @@
 
 private fun areCustomActionsEqual(
     firstAction: PlaybackState.CustomAction,
-    secondAction: PlaybackState.CustomAction
+    secondAction: PlaybackState.CustomAction,
 ): Boolean {
     if (
         firstAction.action != secondAction.action ||
@@ -139,7 +139,7 @@
     context: Context,
     newController: MediaController,
     new: MediaData,
-    old: MediaData
+    old: MediaData,
 ): Boolean {
     val oldState = MediaController(context, old.token!!).playbackState
     return if (
@@ -150,8 +150,7 @@
         var same = true
         new.actions.asSequence().zip(old.actions.asSequence()).forEach {
             if (
-                it.first.actionIntent?.intent?.filterEquals(it.second.actionIntent?.intent) !=
-                    true ||
+                it.first.actionIntent?.intent != it.second.actionIntent?.intent ||
                     it.first.icon != it.second.icon ||
                     it.first.contentDescription != it.second.contentDescription
             ) {
@@ -164,7 +163,7 @@
         oldState?.actions == newController.playbackState?.actions &&
             areCustomActionListsEqual(
                 oldState?.customActions,
-                newController.playbackState?.customActions
+                newController.playbackState?.customActions,
             )
     } else {
         false
@@ -172,8 +171,5 @@
 }
 
 private fun areClickIntentsEqual(newIntent: PendingIntent?, oldIntent: PendingIntent?): Boolean {
-    if ((newIntent == null && oldIntent == null) || newIntent === oldIntent) return true
-    if (newIntent == null || oldIntent == null) return false
-
-    return newIntent.intent?.filterEquals(oldIntent.intent) == true
+    return newIntent == oldIntent
 }
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/MediaProjectionServiceHelper.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/MediaProjectionServiceHelper.kt
index 0b19bab..13a1f95 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/MediaProjectionServiceHelper.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/MediaProjectionServiceHelper.kt
@@ -49,7 +49,8 @@
         fun createOrReuseProjection(
             uid: Int,
             packageName: String,
-            reviewGrantedConsentRequired: Boolean
+            reviewGrantedConsentRequired: Boolean,
+            displayId: Int,
         ): IMediaProjection {
             val existingProjection =
                 if (reviewGrantedConsentRequired) service.getProjection(uid, packageName) else null
@@ -58,7 +59,8 @@
                     uid,
                     packageName,
                     MediaProjectionManager.TYPE_SCREEN_CAPTURE,
-                    false /* permanentGrant */
+                    false /* permanentGrant */,
+                    displayId,
                 )
         }
 
@@ -76,7 +78,7 @@
         fun setReviewedConsentIfNeeded(
             @ReviewGrantedConsentResult consentResult: Int,
             reviewGrantedConsentRequired: Boolean,
-            projection: IMediaProjection?
+            projection: IMediaProjection?,
         ) {
             // Only send the result to the server, when the user needed to review the re-used
             // consent token.
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/data/model/MediaProjectionState.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/data/model/MediaProjectionState.kt
index 82b4825..2fa3405 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/data/model/MediaProjectionState.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/data/model/MediaProjectionState.kt
@@ -32,10 +32,8 @@
      *   media projection. Null if the media projection is going to this same device (e.g. another
      *   app is recording the screen).
      */
-    sealed class Projecting(
-        open val hostPackage: String,
-        open val hostDeviceName: String?,
-    ) : MediaProjectionState {
+    sealed class Projecting(open val hostPackage: String, open val hostDeviceName: String?) :
+        MediaProjectionState {
         /** The entire screen is being projected. */
         data class EntireScreen(
             override val hostPackage: String,
@@ -48,5 +46,11 @@
             override val hostDeviceName: String?,
             val task: RunningTaskInfo,
         ) : Projecting(hostPackage, hostDeviceName)
+
+        /** The screen is not being projected, only audio is being projected. */
+        data class NoScreen(
+            override val hostPackage: String,
+            override val hostDeviceName: String? = null,
+        ) : Projecting(hostPackage, hostDeviceName)
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionManagerRepository.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionManagerRepository.kt
index 5704e80..35efd75 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionManagerRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionManagerRepository.kt
@@ -23,6 +23,7 @@
 import android.os.Handler
 import android.view.ContentRecordingSession
 import android.view.ContentRecordingSession.RECORD_CONTENT_DISPLAY
+import com.android.systemui.Flags
 import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
@@ -94,7 +95,7 @@
                                 {},
                                 { "MediaProjectionManager.Callback#onStart" },
                             )
-                            trySendWithFailureLogging(CallbackEvent.OnStart, TAG)
+                            trySendWithFailureLogging(CallbackEvent.OnStart(info), TAG)
                         }
 
                         override fun onStop(info: MediaProjectionInfo?) {
@@ -109,7 +110,7 @@
 
                         override fun onRecordingSessionSet(
                             info: MediaProjectionInfo,
-                            session: ContentRecordingSession?
+                            session: ContentRecordingSession?,
                         ) {
                             logger.log(
                                 TAG,
@@ -142,7 +143,21 @@
             // #onRecordingSessionSet and we don't emit "Projecting".
             .mapLatest {
                 when (it) {
-                    is CallbackEvent.OnStart,
+                    is CallbackEvent.OnStart -> {
+                        if (!Flags.statusBarShowAudioOnlyProjectionChip()) {
+                            return@mapLatest MediaProjectionState.NotProjecting
+                        }
+                        // It's possible for a projection to be audio-only, in which case `OnStart`
+                        // will occur but `OnRecordingSessionSet` will not. We should still consider
+                        // us to be projecting even if only audio is projecting. See b/373308507.
+                        if (it.info != null) {
+                            MediaProjectionState.Projecting.NoScreen(
+                                hostPackage = it.info.packageName
+                            )
+                        } else {
+                            MediaProjectionState.NotProjecting
+                        }
+                    }
                     is CallbackEvent.OnStop -> MediaProjectionState.NotProjecting
                     is CallbackEvent.OnRecordingSessionSet -> stateForSession(it.info, it.session)
                 }
@@ -155,7 +170,7 @@
 
     private suspend fun stateForSession(
         info: MediaProjectionInfo,
-        session: ContentRecordingSession?
+        session: ContentRecordingSession?,
     ): MediaProjectionState {
         if (session == null) {
             return MediaProjectionState.NotProjecting
@@ -184,7 +199,7 @@
      * the correct callback ordering.
      */
     sealed interface CallbackEvent {
-        data object OnStart : CallbackEvent
+        data class OnStart(val info: MediaProjectionInfo?) : CallbackEvent
 
         data object OnStop : CallbackEvent
 
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/BaseMediaProjectionPermissionDialogDelegate.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/BaseMediaProjectionPermissionDialogDelegate.kt
index cdf8f06..32de56f 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/BaseMediaProjectionPermissionDialogDelegate.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/BaseMediaProjectionPermissionDialogDelegate.kt
@@ -116,7 +116,7 @@
             object : View.AccessibilityDelegate() {
                 override fun onInitializeAccessibilityNodeInfo(
                     host: View,
-                    info: AccessibilityNodeInfo
+                    info: AccessibilityNodeInfo,
                 ) {
                     info.removeAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_LONG_CLICK)
                     super.onInitializeAccessibilityNodeInfo(host, info)
@@ -169,14 +169,11 @@
     }
 }
 
-private class OptionsAdapter(
-    context: Context,
-    private val options: List<ScreenShareOption>,
-) :
+private class OptionsAdapter(context: Context, private val options: List<ScreenShareOption>) :
     ArrayAdapter<String>(
         context,
         R.layout.screen_share_dialog_spinner_text,
-        options.map { context.getString(it.spinnerText) }
+        options.map { context.getString(it.spinnerText, it.displayName) },
     ) {
 
     override fun isEnabled(position: Int): Boolean {
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java
index 212da9f..c70cd0a 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java
@@ -53,6 +53,7 @@
 import android.text.TextPaint;
 import android.text.TextUtils;
 import android.util.Log;
+import android.view.Display;
 import android.view.Window;
 
 import com.android.systemui.flags.FeatureFlags;
@@ -158,8 +159,11 @@
                             mUid, SessionCreationSource.APP);
                 }
                 final IMediaProjection projection =
-                        MediaProjectionServiceHelper.createOrReuseProjection(mUid, mPackageName,
-                                mReviewGrantedConsentRequired);
+                        MediaProjectionServiceHelper.createOrReuseProjection(
+                                mUid,
+                                mPackageName,
+                                mReviewGrantedConsentRequired,
+                                Display.DEFAULT_DISPLAY);
 
                 LaunchCookie launchCookie = launchingIntent.getParcelableExtra(
                         MediaProjectionManager.EXTRA_LAUNCH_COOKIE, LaunchCookie.class);
@@ -279,7 +283,9 @@
                 dialog -> {
                     ScreenShareOption selectedOption = dialog.getSelectedScreenShareOption();
                     grantMediaProjectionPermission(
-                            selectedOption.getMode(), hasCastingCapabilities);
+                            selectedOption.getMode(),
+                            hasCastingCapabilities,
+                            selectedOption.getDisplayId());
                 };
         Runnable onCancelClicked = () -> finish(RECORD_CANCEL, /* projection= */ null);
         if (hasCastingCapabilities) {
@@ -368,10 +374,11 @@
     }
 
     private void grantMediaProjectionPermission(
-            int screenShareMode, boolean hasCastingCapabilities) {
+            int screenShareMode, boolean hasCastingCapabilities, int displayId) {
         try {
-            IMediaProjection projection = MediaProjectionServiceHelper.createOrReuseProjection(
-                    mUid, mPackageName, mReviewGrantedConsentRequired);
+            IMediaProjection projection =
+                    MediaProjectionServiceHelper.createOrReuseProjection(
+                            mUid, mPackageName, mReviewGrantedConsentRequired, displayId);
             if (screenShareMode == ENTIRE_SCREEN) {
                 final Intent intent = new Intent();
                 setCommonIntentExtras(intent, hasCastingCapabilities, projection);
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/ScreenShareOption.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/ScreenShareOption.kt
index ab92173..89383d0 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/ScreenShareOption.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/ScreenShareOption.kt
@@ -15,6 +15,7 @@
  */
 package com.android.systemui.mediaprojection.permission
 
+import android.view.Display
 import androidx.annotation.IntDef
 import androidx.annotation.StringRes
 import kotlin.annotation.Retention
@@ -31,5 +32,7 @@
     @StringRes val spinnerText: Int,
     @StringRes val warningText: Int,
     @StringRes val startButtonText: Int,
+    val displayId: Int = Display.DEFAULT_DISPLAY,
     val spinnerDisabledText: String? = null,
+    val displayName: String? = null,
 )
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/domain/interactor/TaskSwitchInteractor.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/domain/interactor/TaskSwitchInteractor.kt
index 118639c..ccc54f1 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/domain/interactor/TaskSwitchInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/domain/interactor/TaskSwitchInteractor.kt
@@ -68,6 +68,7 @@
                     }
                 }
                 is MediaProjectionState.Projecting.EntireScreen,
+                is MediaProjectionState.Projecting.NoScreen,
                 is MediaProjectionState.NotProjecting -> {
                     flowOf(TaskSwitchState.NotProjectingTask)
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBar.java
index c70a523..96c0cac 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBar.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBar.java
@@ -25,12 +25,10 @@
 import static android.app.StatusBarManager.WindowVisibleState;
 import static android.app.StatusBarManager.windowStateToString;
 import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
-import static android.inputmethodservice.InputMethodService.ENABLE_HIDE_IME_CAPTION_BAR;
 import static android.view.InsetsSource.FLAG_SUPPRESS_SCRIM;
 import static android.view.WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE;
 import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;
-import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
 import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON;
 import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL;
 
@@ -1836,11 +1834,6 @@
     private InsetsFrameProvider[] getInsetsFrameProvider(int insetsHeight, Context userContext) {
         final InsetsFrameProvider navBarProvider =
                 new InsetsFrameProvider(mInsetsSourceOwner, 0, WindowInsets.Type.navigationBars());
-        if (!ENABLE_HIDE_IME_CAPTION_BAR) {
-            navBarProvider.setInsetsSizeOverrides(new InsetsFrameProvider.InsetsSizeOverride[] {
-                    new InsetsFrameProvider.InsetsSizeOverride(TYPE_INPUT_METHOD, null)
-            });
-        }
         if (insetsHeight != -1 && !mEdgeBackGestureHandler.isButtonForcedVisible()) {
             navBarProvider.setInsetsSize(Insets.of(0, 0, 0, insetsHeight));
         }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index d3bed27..602f593 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -116,6 +116,8 @@
     @Nullable
     private View mMediaViewPlaceHolderForScene;
 
+    private boolean mHadConfigurationChangeWhileDetached;
+
     public QSPanel(Context context, AttributeSet attrs) {
         super(context, attrs);
         mUsingMediaPlayer = useQsMediaPlayer(context);
@@ -425,10 +427,23 @@
     @Override
     protected void onConfigurationChanged(Configuration newConfig) {
         super.onConfigurationChanged(newConfig);
+        if (!isAttachedToWindow()) {
+            mHadConfigurationChangeWhileDetached = true;
+        }
         mOnConfigurationChangedListeners.forEach(
                 listener -> listener.onConfigurationChange(newConfig));
     }
 
+    final boolean hadConfigurationChangeWhileDetached() {
+        return mHadConfigurationChangeWhileDetached;
+    }
+
+    @Override
+    protected void onDetachedFromWindow() {
+        super.onDetachedFromWindow();
+        mHadConfigurationChangeWhileDetached = false;
+    }
+
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
index 6cf5b32..85bcc25 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
@@ -234,6 +234,12 @@
             mMediaHost.addVisibilityChangeListener(mMediaHostVisibilityListener);
         }
         mView.addOnConfigurationChangedListener(mOnConfigurationChangedListener);
+        // We were not attached and the configuration may have changed, trigger the listener.
+        if (mView.hadConfigurationChangeWhileDetached()) {
+            mOnConfigurationChangedListener.onConfigurationChange(
+                    getContext().getResources().getConfiguration()
+            );
+        }
         setTiles();
         mLastOrientation = getResources().getConfiguration().orientation;
         mLastScreenLayout = getResources().getConfiguration().screenLayout;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt b/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt
index e4738a2..9c8e84f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt
@@ -59,6 +59,9 @@
 import androidx.compose.runtime.snapshotFlow
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.graphicsLayer
+import androidx.compose.ui.input.pointer.PointerEventPass
+import androidx.compose.ui.input.pointer.PointerInputChange
+import androidx.compose.ui.input.pointer.pointerInput
 import androidx.compose.ui.layout.approachLayout
 import androidx.compose.ui.layout.onPlaced
 import androidx.compose.ui.layout.onSizeChanged
@@ -76,6 +79,7 @@
 import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import androidx.lifecycle.lifecycleScope
 import androidx.lifecycle.repeatOnLifecycle
+import com.android.app.tracing.coroutines.launchTraced as launch
 import com.android.compose.animation.scene.ContentKey
 import com.android.compose.animation.scene.ElementKey
 import com.android.compose.animation.scene.ElementMatcher
@@ -131,7 +135,6 @@
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.collectLatest
 import kotlinx.coroutines.flow.map
-import com.android.app.tracing.coroutines.launchTraced as launch
 
 @SuppressLint("ValidFragment")
 class QSFragmentCompose
@@ -149,11 +152,11 @@
 
     private lateinit var viewModel: QSFragmentComposeViewModel
 
-    private val qsHeight = MutableStateFlow(0)
     private val qqsVisible = MutableStateFlow(false)
     private val qqsPositionOnRoot = Rect()
     private val composeViewPositionOnScreen = Rect()
     private val scrollState = ScrollState(0)
+    private val locationTemp = IntArray(2)
 
     // Inside object for namespacing
     private val notificationScrimClippingParams =
@@ -247,7 +250,11 @@
                             Modifier.notificationScrimClip {
                                 notificationScrimClippingParams.params
                             }
-                        },
+                        }
+                        // Disable touches in the whole composable while the mirror is showing.
+                        // While the mirror is showing, an ancestor of the ComposeView is made
+                        // alpha 0, but touches are still being captured by the composables.
+                        .gesturesDisabled(viewModel.showingMirror),
             ) {
                 val isEditing by
                     viewModel.containerViewModel.editModeViewModel.isEditing
@@ -324,8 +331,27 @@
     }
 
     override fun getQsMinExpansionHeight(): Int {
-        // TODO (b/353253277) implement split screen
-        return viewModel.qqsHeight
+        return if (viewModel.isInSplitShade) {
+            getQsMinExpansionHeightForSplitShade()
+        } else {
+            viewModel.qqsHeight
+        }
+    }
+
+    /**
+     * Returns the min expansion height for split shade.
+     *
+     * On split shade, QS is always expanded and goes from the top of the screen to the bottom of
+     * the QS container.
+     */
+    private fun getQsMinExpansionHeightForSplitShade(): Int {
+        view?.getLocationOnScreen(locationTemp)
+        val top = locationTemp.get(1)
+        // We want to get the original top position, so we subtract any translation currently set.
+        val originalTop = (top - (view?.translationY ?: 0f)).toInt()
+        // On split shade the QS view doesn't start at the top of the screen, so we need to add the
+        // top margin.
+        return originalTop + (view?.height ?: 0)
     }
 
     override fun getDesiredHeight(): Int {
@@ -629,14 +655,15 @@
                         )
                     }
                 }
-            }
-            QuickSettingsTheme {
-                FooterActions(
-                    viewModel = viewModel.footerActionsViewModel,
-                    qsVisibilityLifecycleOwner = this@QSFragmentCompose,
-                    modifier =
-                        Modifier.sysuiResTag("qs_footer_actions").element(ElementKeys.FooterActions),
-                )
+                QuickSettingsTheme {
+                    FooterActions(
+                        viewModel = viewModel.footerActionsViewModel,
+                        qsVisibilityLifecycleOwner = this@QSFragmentCompose,
+                        modifier =
+                            Modifier.sysuiResTag("qs_footer_actions")
+                                .element(ElementKeys.FooterActions),
+                    )
+                }
             }
         }
     }
@@ -871,3 +898,19 @@
         return super.onInterceptTouchEvent(ev)
     }
 }
+
+private fun Modifier.gesturesDisabled(disabled: Boolean) =
+    if (disabled) {
+        pointerInput(Unit) {
+            awaitPointerEventScope {
+                // we should wait for all new pointer events
+                while (true) {
+                    awaitPointerEvent(pass = PointerEventPass.Initial)
+                        .changes
+                        .forEach(PointerInputChange::consume)
+                }
+            }
+        }
+    } else {
+        this
+    }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModel.kt
index d571dd0..d30c6be 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModel.kt
@@ -26,6 +26,7 @@
 import androidx.compose.runtime.setValue
 import androidx.compose.runtime.snapshotFlow
 import androidx.lifecycle.LifecycleCoroutineScope
+import com.android.app.tracing.coroutines.launchTraced as launch
 import com.android.keyguard.BouncerPanelExpansionCalculator
 import com.android.systemui.Dumpable
 import com.android.systemui.animation.ShadeInterpolation
@@ -65,13 +66,12 @@
 import kotlinx.coroutines.coroutineScope
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.onStart
-import com.android.app.tracing.coroutines.launchTraced as launch
 
 @OptIn(ExperimentalCoroutinesApi::class)
 class QSFragmentComposeViewModel
 @AssistedInject
 constructor(
-    val containerViewModel: QuickSettingsContainerViewModel,
+    containerViewModelFactory: QuickSettingsContainerViewModel.Factory,
     @Main private val resources: Resources,
     footerActionsViewModelFactory: FooterActionsViewModel.Factory,
     private val footerActionsController: FooterActionsController,
@@ -87,6 +87,8 @@
     @Assisted private val lifecycleScope: LifecycleCoroutineScope,
 ) : Dumpable, ExclusiveActivatable() {
 
+    val containerViewModel = containerViewModelFactory.create(true)
+
     private val hydrator = Hydrator("QSFragmentComposeViewModel.hydrator")
 
     val footerActionsViewModel =
@@ -257,6 +259,9 @@
                     .onStart { emit(sysuiStatusBarStateController.state) },
         )
 
+    val showingMirror: Boolean
+        get() = containerViewModel.brightnessSliderViewModel.showMirror
+
     private val isKeyguardState: Boolean
         get() = statusBarState == StatusBarState.KEYGUARD
 
@@ -321,6 +326,7 @@
         coroutineScope {
             launch { hydrateSquishinessInteractor() }
             launch { hydrator.activate() }
+            launch { containerViewModel.activate() }
             awaitCancellation()
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/RecordIssueTile.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/RecordIssueTile.kt
index 1792ebd..028ac6f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/RecordIssueTile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/RecordIssueTile.kt
@@ -111,10 +111,12 @@
 
     override fun handleSetListening(listening: Boolean) {
         super.handleSetListening(listening)
-        if (listening) {
-            issueRecordingState.addListener(onRecordingChangeListener)
-        } else {
-            issueRecordingState.removeListener(onRecordingChangeListener)
+        bgExecutor.execute {
+            if (listening) {
+                issueRecordingState.addListener(onRecordingChangeListener)
+            } else {
+                issueRecordingState.removeListener(onRecordingChangeListener)
+            }
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingDataInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingDataInteractor.kt
index 1af328e..09a6ce8 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingDataInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingDataInteractor.kt
@@ -41,7 +41,7 @@
 
     override fun tileData(
         user: UserHandle,
-        triggers: Flow<DataUpdateTrigger>
+        triggers: Flow<DataUpdateTrigger>,
     ): Flow<IssueRecordingModel> =
         conflatedCallbackFlow {
                 val listener = Runnable { trySend(IssueRecordingModel(state.isRecording)) }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsContainerViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsContainerViewModel.kt
index 6ccd169..b1eb3bb3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsContainerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsContainerViewModel.kt
@@ -17,18 +17,33 @@
 package com.android.systemui.qs.ui.viewmodel
 
 import com.android.systemui.brightness.ui.viewmodel.BrightnessSliderViewModel
-import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.lifecycle.ExclusiveActivatable
 import com.android.systemui.qs.panels.ui.viewmodel.EditModeViewModel
 import com.android.systemui.qs.panels.ui.viewmodel.QuickQuickSettingsViewModel
 import com.android.systemui.qs.panels.ui.viewmodel.TileGridViewModel
-import javax.inject.Inject
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
 
-@SysUISingleton
 class QuickSettingsContainerViewModel
-@Inject
+@AssistedInject
 constructor(
-    val brightnessSliderViewModel: BrightnessSliderViewModel,
+    brightnessSliderViewModelFactory: BrightnessSliderViewModel.Factory,
+    @Assisted supportsBrightnessMirroring: Boolean,
     val tileGridViewModel: TileGridViewModel,
     val editModeViewModel: EditModeViewModel,
     val quickQuickSettingsViewModel: QuickQuickSettingsViewModel,
-)
+) : ExclusiveActivatable() {
+
+    val brightnessSliderViewModel =
+        brightnessSliderViewModelFactory.create(supportsBrightnessMirroring)
+
+    override suspend fun onActivated(): Nothing {
+        brightnessSliderViewModel.activate()
+    }
+
+    @AssistedFactory
+    interface Factory {
+        fun create(supportsBrightnessMirroring: Boolean): QuickSettingsContainerViewModel
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayActionsViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayActionsViewModel.kt
index 31519a9..9a416d1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayActionsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayActionsViewModel.kt
@@ -23,6 +23,7 @@
 import com.android.compose.animation.scene.UserActionResult
 import com.android.compose.animation.scene.UserActionResult.HideOverlay
 import com.android.compose.animation.scene.UserActionResult.ReplaceByOverlay
+import com.android.systemui.qs.panels.ui.viewmodel.EditModeViewModel
 import com.android.systemui.scene.shared.model.Overlays
 import com.android.systemui.scene.ui.viewmodel.SceneContainerEdge
 import com.android.systemui.scene.ui.viewmodel.UserActionsViewModel
@@ -33,11 +34,10 @@
 /** Models the UI state for the user actions for navigating to other scenes or overlays. */
 class QuickSettingsShadeOverlayActionsViewModel
 @AssistedInject
-constructor(private val containerViewModel: QuickSettingsContainerViewModel) :
-    UserActionsViewModel() {
+constructor(private val editModeViewModel: EditModeViewModel) : UserActionsViewModel() {
 
     override suspend fun hydrateActions(setActions: (Map<UserAction, UserActionResult>) -> Unit) {
-        containerViewModel.editModeViewModel.isEditing
+        editModeViewModel.isEditing
             .map { isEditing ->
                 buildMap {
                     put(Swipe.Up, HideOverlay(Overlays.QuickSettingsShade))
diff --git a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayContentViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayContentViewModel.kt
index bed8574..8ef51af 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayContentViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayContentViewModel.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.qs.ui.viewmodel
 
+import com.android.app.tracing.coroutines.launchTraced as launch
 import com.android.systemui.lifecycle.ExclusiveActivatable
 import com.android.systemui.scene.domain.interactor.SceneInteractor
 import com.android.systemui.scene.shared.model.Scenes
@@ -27,7 +28,6 @@
 import kotlinx.coroutines.coroutineScope
 import kotlinx.coroutines.flow.distinctUntilChanged
 import kotlinx.coroutines.flow.filter
-import com.android.app.tracing.coroutines.launchTraced as launch
 
 /**
  * Models UI state used to render the content of the quick settings shade overlay.
@@ -41,9 +41,11 @@
     val shadeInteractor: ShadeInteractor,
     val sceneInteractor: SceneInteractor,
     val shadeHeaderViewModelFactory: ShadeHeaderViewModel.Factory,
-    val quickSettingsContainerViewModel: QuickSettingsContainerViewModel,
+    quickSettingsContainerViewModelFactory: QuickSettingsContainerViewModel.Factory,
 ) : ExclusiveActivatable() {
 
+    val quickSettingsContainerViewModel = quickSettingsContainerViewModelFactory.create(false)
+
     override suspend fun onActivated(): Nothing {
         coroutineScope {
             launch {
@@ -68,6 +70,8 @@
                         )
                     }
             }
+
+            launch { quickSettingsContainerViewModel.activate() }
         }
 
         awaitCancellation()
diff --git a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneContentViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneContentViewModel.kt
deleted file mode 100644
index d01b33b..0000000
--- a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneContentViewModel.kt
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.qs.ui.viewmodel
-
-import dagger.assisted.AssistedFactory
-import dagger.assisted.AssistedInject
-
-/**
- * Models UI state used to render the content of the quick settings shade scene.
- *
- * Different from [QuickSettingsShadeUserActionsViewModel], which only models user actions that can
- * be performed to navigate to other scenes.
- */
-class QuickSettingsShadeSceneContentViewModel
-@AssistedInject
-constructor(
-    val quickSettingsContainerViewModel: QuickSettingsContainerViewModel,
-) {
-
-    @AssistedFactory
-    interface Factory {
-        fun create(): QuickSettingsShadeSceneContentViewModel
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeUserActionsViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeUserActionsViewModel.kt
deleted file mode 100644
index bd1872d..0000000
--- a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeUserActionsViewModel.kt
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.qs.ui.viewmodel
-
-import com.android.compose.animation.scene.Back
-import com.android.compose.animation.scene.Swipe
-import com.android.compose.animation.scene.SwipeDirection
-import com.android.compose.animation.scene.UserAction
-import com.android.compose.animation.scene.UserActionResult
-import com.android.compose.animation.scene.UserActionResult.ReplaceByOverlay
-import com.android.systemui.scene.shared.model.Overlays
-import com.android.systemui.scene.shared.model.SceneFamilies
-import com.android.systemui.scene.ui.viewmodel.SceneContainerEdge
-import com.android.systemui.scene.ui.viewmodel.UserActionsViewModel
-import dagger.assisted.AssistedFactory
-import dagger.assisted.AssistedInject
-import kotlinx.coroutines.flow.map
-
-/**
- * Models the UI state for the user actions that the user can perform to navigate to other scenes.
- *
- * Different from the [QuickSettingsShadeSceneContentViewModel] which models the _content_ of the
- * scene.
- */
-class QuickSettingsShadeUserActionsViewModel
-@AssistedInject
-constructor(
-    val quickSettingsContainerViewModel: QuickSettingsContainerViewModel,
-) : UserActionsViewModel() {
-
-    override suspend fun hydrateActions(setActions: (Map<UserAction, UserActionResult>) -> Unit) {
-        quickSettingsContainerViewModel.editModeViewModel.isEditing
-            .map { editing ->
-                buildMap {
-                    put(Swipe.Up, UserActionResult(SceneFamilies.Home))
-                    put(
-                        Swipe(
-                            direction = SwipeDirection.Down,
-                            fromSource = SceneContainerEdge.TopLeft
-                        ),
-                        ReplaceByOverlay(Overlays.NotificationsShade)
-                    )
-                    if (!editing) {
-                        put(Back, UserActionResult(SceneFamilies.Home))
-                    }
-                }
-            }
-            .collect { actions -> setActions(actions) }
-    }
-
-    @AssistedFactory
-    interface Factory {
-        fun create(): QuickSettingsShadeUserActionsViewModel
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingServiceSession.kt b/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingServiceSession.kt
index 4353933..16ca81a 100644
--- a/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingServiceSession.kt
+++ b/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingServiceSession.kt
@@ -55,8 +55,10 @@
     var screenRecord = false
 
     fun start() {
-        bgExecutor.execute { traceurConnection.startTracing(traceConfig) }
-        issueRecordingState.isRecording = true
+        bgExecutor.execute {
+            traceurConnection.startTracing(traceConfig)
+            issueRecordingState.isRecording = true
+        }
     }
 
     fun stop() {
@@ -69,8 +71,8 @@
                 )
             }
             traceurConnection.stopTracing()
+            issueRecordingState.isRecording = false
         }
-        issueRecordingState.isRecording = false
     }
 
     fun share(notificationId: Int, screenRecording: Uri?) {
diff --git a/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingState.kt b/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingState.kt
index 51c437dbe..ffdcf1b 100644
--- a/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingState.kt
+++ b/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingState.kt
@@ -16,12 +16,20 @@
 
 package com.android.systemui.recordissue
 
+import android.annotation.SuppressLint
+import android.content.ContentResolver
 import android.content.Context
+import android.database.ContentObserver
+import android.os.Handler
+import androidx.annotation.VisibleForTesting
+import androidx.annotation.WorkerThread
 import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.recordissue.RecordIssueModule.Companion.TILE_SPEC
 import com.android.systemui.res.R
 import com.android.systemui.settings.UserFileManager
 import com.android.systemui.settings.UserTracker
+import com.android.systemui.util.settings.GlobalSettings
 import com.android.traceur.PresetTraceConfigs
 import com.android.traceur.TraceConfig
 import java.util.concurrent.CopyOnWriteArrayList
@@ -31,12 +39,20 @@
 class IssueRecordingState
 @Inject
 constructor(
-    userTracker: UserTracker,
-    userFileManager: UserFileManager,
+    private val userTracker: UserTracker,
+    private val userFileManager: UserFileManager,
+    @Background bgHandler: Handler,
+    private val resolver: ContentResolver,
+    private val globalSettings: GlobalSettings,
 ) {
 
-    private val prefs =
-        userFileManager.getSharedPreferences(TILE_SPEC, Context.MODE_PRIVATE, userTracker.userId)
+    private val prefs
+        get() =
+            userFileManager.getSharedPreferences(
+                TILE_SPEC,
+                Context.MODE_PRIVATE,
+                userTracker.userId,
+            )
 
     val customTraceState = CustomTraceState(prefs)
 
@@ -77,22 +93,52 @@
 
     private val listeners = CopyOnWriteArrayList<Runnable>()
 
+    @VisibleForTesting
+    val onRecordingChangeListener =
+        object : ContentObserver(bgHandler) {
+            override fun onChange(selfChange: Boolean) {
+                isRecording = globalSettings.getInt(KEY_ONGOING_ISSUE_RECORDING, 0) == 1
+                listeners.forEach(Runnable::run)
+            }
+        }
+
+    /**
+     * isRecording is purposely always set to false at the initialization of the record issue qs
+     * tile. We want to avoid a situation where the System UI crashed / the device was restarted in
+     * the middle of a trace session and the QS tile is in an active state even though no tracing is
+     * ongoing.
+     */
     var isRecording = false
+        @WorkerThread
         set(value) {
+            globalSettings.putInt(KEY_ONGOING_ISSUE_RECORDING, if (value) 1 else 0)
             field = value
-            listeners.forEach(Runnable::run)
         }
 
     fun markUserApprovalForScreenRecording() {
         hasUserApprovedScreenRecording = true
     }
 
+    @WorkerThread
+    @SuppressLint("RegisterContentObserverViaContentResolver")
     fun addListener(listener: Runnable) {
+        if (listeners.isEmpty()) {
+            resolver.registerContentObserver(
+                globalSettings.getUriFor(KEY_ONGOING_ISSUE_RECORDING),
+                false,
+                onRecordingChangeListener,
+            )
+        }
         listeners.add(listener)
     }
 
+    @WorkerThread
+    @SuppressLint("RegisterContentObserverViaContentResolver")
     fun removeListener(listener: Runnable) {
         listeners.remove(listener)
+        if (listeners.isEmpty()) {
+            resolver.unregisterContentObserver(onRecordingChangeListener)
+        }
     }
 
     companion object {
@@ -100,6 +146,7 @@
         private const val HAS_APPROVED_SCREEN_RECORDING = "HasApprovedScreenRecord"
         private const val KEY_RECORD_SCREEN = "key_recordScreen"
         private const val KEY_TAG_TITLES = "key_tagTitles"
+        private const val KEY_ONGOING_ISSUE_RECORDING = "issueRecordingOngoing"
         const val KEY_ISSUE_TYPE_INDEX = "key_issueTypeIndex"
         const val ISSUE_TYPE_NOT_SET = -1
         const val TAG_TITLE_DELIMITER = ": "
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/ScrimStartable.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/ScrimStartable.kt
index 55033f0..1d97034 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/ScrimStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/ScrimStartable.kt
@@ -185,6 +185,7 @@
                 } else if (isOnKeyguard && !unlocking && isDreaming) {
                     Model(scrimState = ScrimState.DREAMING, unlocking = false)
                 } else {
+                    onKeyguardFadedAway(transitionState.isIdle(Scenes.Gone))
                     Model(scrimState = ScrimState.UNLOCKED, unlocking = unlocking)
                 }
             }
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
index 6cc9ae4..8c207d1 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
@@ -36,6 +36,7 @@
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.util.Log;
+import android.view.Display;
 import android.widget.Toast;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -76,6 +77,7 @@
     private static final String EXTRA_AUDIO_SOURCE = "extra_useAudio";
     private static final String EXTRA_SHOW_TAPS = "extra_showTaps";
     private static final String EXTRA_CAPTURE_TARGET = "extra_captureTarget";
+    private static final String EXTRA_DISPLAY_ID = "extra_displayId";
 
     protected static final String ACTION_START = "com.android.systemui.screenrecord.START";
     protected static final String ACTION_SHOW_START_NOTIF =
@@ -141,6 +143,30 @@
                 .putExtra(EXTRA_CAPTURE_TARGET, captureTarget);
     }
 
+    /**
+     * Get an intent to start the recording service.
+     *
+     * @param context Context from the requesting activity
+     * @param resultCode The result code from {@link android.app.Activity#onActivityResult(int, int,
+     *     android.content.Intent)}
+     * @param audioSource The ordinal value of the audio source {@link
+     *     com.android.systemui.screenrecord.ScreenRecordingAudioSource}
+     * @param showTaps True to make touches visible while recording
+     * @param captureTarget pass this parameter to capture a specific part instead of the full
+     *     screen
+     * @param displayId The id of the display to record.
+     */
+    public static Intent getStartIntent(
+            Context context,
+            int resultCode,
+            int audioSource,
+            boolean showTaps,
+            int displayId,
+            @Nullable MediaProjectionCaptureTarget captureTarget) {
+        return getStartIntent(context, resultCode, audioSource, showTaps, captureTarget)
+                .putExtra(EXTRA_DISPLAY_ID, displayId);
+    }
+
     @Override
     public int onStartCommand(Intent intent, int flags, int startId) {
         if (intent == null) {
@@ -174,6 +200,7 @@
                 mOriginalShowTaps = Settings.System.getInt(
                         getApplicationContext().getContentResolver(),
                         Settings.System.SHOW_TOUCHES, 0) != 0;
+                int displayId = intent.getIntExtra(EXTRA_DISPLAY_ID, Display.DEFAULT_DISPLAY);
 
                 setTapsVisible(mShowTaps);
 
@@ -183,6 +210,7 @@
                         currentUid,
                         mAudioSource,
                         captureTarget,
+                        displayId,
                         this,
                         mScreenRecordingStartTimeStore
                 );
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java
index 54da1b0..2ca0621 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java
@@ -50,8 +50,8 @@
 import android.util.DisplayMetrics;
 import android.util.Log;
 import android.util.Size;
+import android.view.Display;
 import android.view.Surface;
-import android.view.WindowManager;
 
 import com.android.internal.R;
 import com.android.systemui.mediaprojection.MediaProjectionCaptureTarget;
@@ -94,13 +94,18 @@
     private final MediaProjectionCaptureTarget mCaptureRegion;
     private final ScreenRecordingStartTimeStore mScreenRecordingStartTimeStore;
     private final Handler mHandler;
+    private final int mDisplayId;
 
     private Context mContext;
     ScreenMediaRecorderListener mListener;
 
-    public ScreenMediaRecorder(Context context, Handler handler,
-            int uid, ScreenRecordingAudioSource audioSource,
+    public ScreenMediaRecorder(
+            Context context,
+            Handler handler,
+            int uid,
+            ScreenRecordingAudioSource audioSource,
             MediaProjectionCaptureTarget captureRegion,
+            int displayId,
             ScreenMediaRecorderListener listener,
             ScreenRecordingStartTimeStore screenRecordingStartTimeStore) {
         mContext = context;
@@ -109,6 +114,7 @@
         mCaptureRegion = captureRegion;
         mListener = listener;
         mAudioSource = audioSource;
+        mDisplayId = displayId;
         mScreenRecordingStartTimeStore = screenRecordingStartTimeStore;
     }
 
@@ -117,9 +123,13 @@
         IBinder b = ServiceManager.getService(MEDIA_PROJECTION_SERVICE);
         IMediaProjectionManager mediaService =
                 IMediaProjectionManager.Stub.asInterface(b);
-        IMediaProjection proj = null;
-        proj = mediaService.createProjection(mUid, mContext.getPackageName(),
-                    MediaProjectionManager.TYPE_SCREEN_CAPTURE, false);
+        IMediaProjection proj =
+                mediaService.createProjection(
+                        mUid,
+                        mContext.getPackageName(),
+                        MediaProjectionManager.TYPE_SCREEN_CAPTURE,
+                        false,
+                        mDisplayId);
         IMediaProjection projection = IMediaProjection.Stub.asInterface(proj.asBinder());
         if (mCaptureRegion != null) {
             projection.setLaunchCookie(mCaptureRegion.getLaunchCookie());
@@ -146,9 +156,10 @@
 
         // Set up video
         DisplayMetrics metrics = new DisplayMetrics();
-        WindowManager wm = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
-        wm.getDefaultDisplay().getRealMetrics(metrics);
-        int refreshRate = (int) wm.getDefaultDisplay().getRefreshRate();
+        DisplayManager dm = mContext.getSystemService(DisplayManager.class);
+        Display display = dm.getDisplay(mDisplayId);
+        display.getRealMetrics(metrics);
+        int refreshRate = (int) display.getRefreshRate();
         int[] dimens = getSupportedSize(metrics.widthPixels, metrics.heightPixels, refreshRate);
         int width = dimens[0];
         int height = dimens[1];
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogDelegate.kt b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogDelegate.kt
index f3357ee..bdc58c1 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogDelegate.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogDelegate.kt
@@ -20,11 +20,14 @@
 import android.app.PendingIntent
 import android.content.Context
 import android.content.Intent
+import android.hardware.display.DisplayManager
+import android.os.Build
 import android.os.Bundle
 import android.os.Handler
 import android.os.Looper
 import android.os.ResultReceiver
 import android.os.UserHandle
+import android.view.Display
 import android.view.MotionEvent.ACTION_MOVE
 import android.view.View
 import android.view.View.GONE
@@ -66,9 +69,10 @@
     @ScreenShareMode defaultSelectedMode: Int,
     @StyleRes private val theme: Int,
     private val context: Context,
+    displayManager: DisplayManager,
 ) :
     BaseMediaProjectionPermissionDialogDelegate<SystemUIDialog>(
-        createOptionList(),
+        createOptionList(displayManager),
         appName = null,
         hostUid = hostUid,
         mediaProjectionMetricsLogger,
@@ -88,6 +92,7 @@
         mediaProjectionMetricsLogger: MediaProjectionMetricsLogger,
         systemUIDialogFactory: SystemUIDialog.Factory,
         @Application context: Context,
+        displayManager: DisplayManager,
     ) : this(
         hostUserHandle,
         hostUid,
@@ -100,6 +105,7 @@
         defaultSelectedMode = SINGLE_APP,
         theme = SystemUIDialog.DEFAULT_THEME,
         context,
+        displayManager,
     )
 
     @AssistedFactory
@@ -128,7 +134,7 @@
         setStartButtonOnClickListener { v: View? ->
             onStartRecordingClicked?.run()
             if (selectedScreenShareOption.mode == ENTIRE_SCREEN) {
-                requestScreenCapture(/* captureTarget= */ null)
+                requestScreenCapture(/* captureTarget= */ null, selectedScreenShareOption.displayId)
             }
             if (selectedScreenShareOption.mode == SINGLE_APP) {
                 val intent = Intent(dialog.context, MediaProjectionAppSelectorActivity::class.java)
@@ -138,12 +144,12 @@
                 // the selected target to capture
                 intent.putExtra(
                     MediaProjectionAppSelectorActivity.EXTRA_CAPTURE_REGION_RESULT_RECEIVER,
-                    CaptureTargetResultReceiver()
+                    CaptureTargetResultReceiver(),
                 )
 
                 intent.putExtra(
                     MediaProjectionAppSelectorActivity.EXTRA_HOST_APP_USER_HANDLE,
-                    hostUserHandle
+                    hostUserHandle,
                 )
                 intent.putExtra(MediaProjectionAppSelectorActivity.EXTRA_HOST_APP_UID, hostUid)
                 intent.putExtra(
@@ -178,7 +184,7 @@
             ScreenRecordingAdapter(
                 dialog.context,
                 android.R.layout.simple_spinner_dropdown_item,
-                MODES
+                MODES,
             )
         a.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
         options.adapter = a
@@ -191,7 +197,7 @@
             object : View.AccessibilityDelegate() {
                 override fun onInitializeAccessibilityNodeInfo(
                     host: View,
-                    info: AccessibilityNodeInfo
+                    info: AccessibilityNodeInfo,
                 ) {
                     info.removeAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_LONG_CLICK)
                     super.onInitializeAccessibilityNodeInfo(host, info)
@@ -215,7 +221,10 @@
      * @param captureTarget target to capture (could be e.g. a task) or null to record the whole
      *   screen
      */
-    private fun requestScreenCapture(captureTarget: MediaProjectionCaptureTarget?) {
+    private fun requestScreenCapture(
+        captureTarget: MediaProjectionCaptureTarget?,
+        displayId: Int = Display.DEFAULT_DISPLAY,
+    ) {
         val userContext = userContextProvider.userContext
         val showTaps = selectedScreenShareOption.mode != SINGLE_APP && tapsSwitch.isChecked
         val audioMode =
@@ -230,28 +239,29 @@
                     Activity.RESULT_OK,
                     audioMode.ordinal,
                     showTaps,
-                    captureTarget
+                    displayId,
+                    captureTarget,
                 ),
-                PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
+                PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE,
             )
         val stopIntent =
             PendingIntent.getService(
                 userContext,
                 RecordingService.REQUEST_CODE,
                 RecordingService.getStopIntent(userContext),
-                PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
+                PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE,
             )
         controller.startCountdown(DELAY_MS, INTERVAL_MS, startIntent, stopIntent)
     }
 
-    private inner class CaptureTargetResultReceiver() :
+    private inner class CaptureTargetResultReceiver :
         ResultReceiver(Handler(Looper.getMainLooper())) {
         override fun onReceiveResult(resultCode: Int, resultData: Bundle) {
             if (resultCode == Activity.RESULT_OK) {
                 val captureTarget =
                     resultData.getParcelable(
                         MediaProjectionAppSelectorActivity.KEY_CAPTURE_TARGET,
-                        MediaProjectionCaptureTarget::class.java
+                        MediaProjectionCaptureTarget::class.java,
                     )
 
                 // Start recording of the selected target
@@ -265,12 +275,33 @@
             listOf(
                 ScreenRecordingAudioSource.INTERNAL,
                 ScreenRecordingAudioSource.MIC,
-                ScreenRecordingAudioSource.MIC_AND_INTERNAL
+                ScreenRecordingAudioSource.MIC_AND_INTERNAL,
             )
         private const val DELAY_MS: Long = 3000
         private const val INTERVAL_MS: Long = 1000
 
-        private fun createOptionList(): List<ScreenShareOption> {
+        private fun createOptionList(displayManager: DisplayManager): List<ScreenShareOption> {
+            if (!com.android.media.projection.flags.Flags.mediaProjectionConnectedDisplay()) {
+                return listOf(
+                    ScreenShareOption(
+                        SINGLE_APP,
+                        R.string.screenrecord_permission_dialog_option_text_single_app,
+                        R.string.screenrecord_permission_dialog_warning_single_app,
+                        startButtonText =
+                            R.string
+                                .media_projection_entry_generic_permission_dialog_continue_single_app,
+                    ),
+                    ScreenShareOption(
+                        ENTIRE_SCREEN,
+                        R.string.screenrecord_permission_dialog_option_text_entire_screen,
+                        R.string.screenrecord_permission_dialog_warning_entire_screen,
+                        startButtonText =
+                            R.string.screenrecord_permission_dialog_continue_entire_screen,
+                        displayId = Display.DEFAULT_DISPLAY,
+                        displayName = Build.MODEL,
+                    ),
+                )
+            }
             return listOf(
                 ScreenShareOption(
                     SINGLE_APP,
@@ -282,12 +313,31 @@
                 ),
                 ScreenShareOption(
                     ENTIRE_SCREEN,
-                    R.string.screenrecord_permission_dialog_option_text_entire_screen,
+                    R.string.screenrecord_permission_dialog_option_text_entire_screen_for_display,
                     R.string.screenrecord_permission_dialog_warning_entire_screen,
                     startButtonText =
                         R.string.screenrecord_permission_dialog_continue_entire_screen,
-                )
-            )
+                    displayId = Display.DEFAULT_DISPLAY,
+                    displayName = Build.MODEL,
+                ),
+            ) +
+                displayManager.displays
+                    .filter { it.displayId != Display.DEFAULT_DISPLAY }
+                    .map {
+                        ScreenShareOption(
+                            ENTIRE_SCREEN,
+                            R.string
+                                .screenrecord_permission_dialog_option_text_entire_screen_for_display,
+                            warningText =
+                                R.string
+                                    .media_projection_entry_app_permission_dialog_warning_entire_screen,
+                            startButtonText =
+                                R.string
+                                    .media_projection_entry_app_permission_dialog_continue_entire_screen,
+                            displayId = it.displayId,
+                            displayName = it.name,
+                        )
+                    }
         }
     }
 }
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 969cf48..b8ea8f9 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ui/ScreenshotShelfView.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ui/ScreenshotShelfView.kt
@@ -28,6 +28,7 @@
 import android.view.View
 import android.view.ViewGroup
 import android.view.WindowInsets
+import android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL
 import android.widget.FrameLayout
 import android.widget.ImageView
 import com.android.systemui.res.R
@@ -59,17 +60,17 @@
                     ev1: MotionEvent?,
                     ev2: MotionEvent,
                     distanceX: Float,
-                    distanceY: Float
+                    distanceY: Float,
                 ): Boolean {
                     actionsContainer.getBoundsOnScreen(tmpRect)
                     val touchedInActionsContainer =
                         tmpRect.contains(ev2.rawX.toInt(), ev2.rawY.toInt())
                     val canHandleInternallyByScrolling =
-                        touchedInActionsContainer
-                        && actionsContainer.canScrollHorizontally(distanceX.toInt())
+                        touchedInActionsContainer &&
+                            actionsContainer.canScrollHorizontally(distanceX.toInt())
                     return !canHandleInternallyByScrolling
                 }
-            }
+            },
         )
 
     init {
@@ -106,18 +107,24 @@
     fun getTouchRegion(gestureInsets: Insets): Region {
         val region = getSwipeRegion()
 
-        // Receive touches in gesture insets so they don't cause TOUCH_OUTSIDE
-        // left edge gesture region
-        val insetRect = Rect(0, 0, gestureInsets.left, displayMetrics.heightPixels)
-        region.op(insetRect, Region.Op.UNION)
-        // right edge gesture region
-        insetRect.set(
-            displayMetrics.widthPixels - gestureInsets.right,
-            0,
-            displayMetrics.widthPixels,
-            displayMetrics.heightPixels
-        )
-        region.op(insetRect, Region.Op.UNION)
+        // only add gesture insets to touch region in gestural mode
+        if (
+            resources.getInteger(com.android.internal.R.integer.config_navBarInteractionMode) ==
+                NAV_BAR_MODE_GESTURAL
+        ) {
+            // Receive touches in gesture insets so they don't cause TOUCH_OUTSIDE
+            // left edge gesture region
+            val insetRect = Rect(0, 0, gestureInsets.left, displayMetrics.heightPixels)
+            region.op(insetRect, Region.Op.UNION)
+            // right edge gesture region
+            insetRect.set(
+                displayMetrics.widthPixels - gestureInsets.right,
+                0,
+                displayMetrics.widthPixels,
+                displayMetrics.heightPixels,
+            )
+            region.op(insetRect, Region.Op.UNION)
+        }
 
         return region
     }
@@ -153,7 +160,7 @@
                         cutout.safeInsetBottom + verticalPadding,
                         waterfall.bottom + verticalPadding,
                         minimumBottomPadding,
-                    )
+                    ),
                 )
             } else {
                 screenshotStatic.setPadding(
@@ -164,7 +171,7 @@
                         navBarInsets.bottom + verticalPadding,
                         waterfall.bottom + verticalPadding,
                         minimumBottomPadding,
-                    )
+                    ),
                 )
             }
         }
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java
index a62edcb..8c004c4 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java
@@ -39,10 +39,16 @@
 import android.view.accessibility.AccessibilityManager;
 import android.widget.FrameLayout;
 
+import androidx.annotation.NonNull;
+import androidx.compose.ui.platform.ComposeView;
+
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.systemui.brightness.ui.viewmodel.BrightnessSliderViewModel;
+import com.android.systemui.compose.ComposeInitializer;
 import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.qs.flags.QSComposeFragment;
 import com.android.systemui.res.R;
 import com.android.systemui.shade.domain.interactor.ShadeInteractor;
 import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
@@ -65,6 +71,7 @@
     private final AccessibilityManagerWrapper mAccessibilityMgr;
     private Runnable mCancelTimeoutRunnable;
     private final ShadeInteractor mShadeInteractor;
+    private final BrightnessSliderViewModel.Factory mBrightnessSliderViewModelFactory;
 
     @Inject
     public BrightnessDialog(
@@ -72,13 +79,15 @@
             BrightnessController.Factory brightnessControllerFactory,
             @Main DelayableExecutor mainExecutor,
             AccessibilityManagerWrapper accessibilityMgr,
-            ShadeInteractor shadeInteractor
+            ShadeInteractor shadeInteractor,
+            BrightnessSliderViewModel.Factory brightnessSliderViewModelFactory
     ) {
         mToggleSliderFactory = brightnessSliderfactory;
         mBrightnessControllerFactory = brightnessControllerFactory;
         mMainExecutor = mainExecutor;
         mAccessibilityMgr = accessibilityMgr;
         mShadeInteractor = shadeInteractor;
+        mBrightnessSliderViewModelFactory = brightnessSliderViewModelFactory;
     }
 
 
@@ -86,14 +95,28 @@
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         setWindowAttributes();
-        setContentView(R.layout.brightness_mirror_container);
-        setBrightnessDialogViewAttributes();
+        View view;
+        if (!QSComposeFragment.isEnabled()) {
+            setContentView(R.layout.brightness_mirror_container);
+            view = findViewById(R.id.brightness_mirror_container);
+            setDialogContent((FrameLayout) view);
+        } else {
+            ComposeView composeView = new ComposeView(this);
+            ComposeDialogComposableProvider.INSTANCE.setComposableBrightness(
+                    composeView,
+                    new ComposableProvider(mBrightnessSliderViewModelFactory)
+            );
+            composeView.setId(R.id.brightness_dialog_slider);
+            setContentView(composeView);
+            ((ViewGroup) composeView.getParent()).setClipChildren(false);
+            view = composeView;
+        }
+        setBrightnessDialogViewAttributes(view);
 
         if (mShadeInteractor.isQsExpanded().getValue()) {
             finish();
         }
 
-        View view = findViewById(R.id.brightness_mirror_container);
         if (view != null) {
             collectFlow(view, mShadeInteractor.isQsExpanded(), this::onShadeStateChange);
         }
@@ -117,13 +140,27 @@
         window.getDecorView();
         window.setLayout(WRAP_CONTENT, WRAP_CONTENT);
         getTheme().applyStyle(R.style.Theme_SystemUI_QuickSettings, false);
+        if (QSComposeFragment.isEnabled()) {
+            window.getDecorView().addOnAttachStateChangeListener(
+                    new View.OnAttachStateChangeListener() {
+                        @Override
+                        public void onViewAttachedToWindow(@NonNull View v) {
+                            ComposeInitializer.INSTANCE.onAttachedToWindow(v);
+                        }
+
+                        @Override
+                        public void onViewDetachedFromWindow(@NonNull View v) {
+                            ComposeInitializer.INSTANCE.onDetachedFromWindow(v);
+                        }
+                    });
+        }
     }
 
-    void setBrightnessDialogViewAttributes() {
-        FrameLayout frame = findViewById(R.id.brightness_mirror_container);
+    void setBrightnessDialogViewAttributes(View container) {
         // The brightness mirror container is INVISIBLE by default.
-        frame.setVisibility(View.VISIBLE);
-        ViewGroup.MarginLayoutParams lp = (ViewGroup.MarginLayoutParams) frame.getLayoutParams();
+        container.setVisibility(View.VISIBLE);
+        ViewGroup.MarginLayoutParams lp =
+                (ViewGroup.MarginLayoutParams) container.getLayoutParams();
         int horizontalMargin =
                 getResources().getDimensionPixelSize(R.dimen.notification_side_paddings);
         lp.leftMargin = horizontalMargin;
@@ -136,23 +173,6 @@
         lp.topMargin = verticalMargin;
         lp.bottomMargin = verticalMargin;
 
-        frame.setLayoutParams(lp);
-        Rect bounds = new Rect();
-        frame.addOnLayoutChangeListener(
-                (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
-                    // Exclude this view (and its horizontal margins) from triggering gestures.
-                    // This prevents back gesture from being triggered by dragging close to the
-                    // edge of the slider (0% or 100%).
-                    bounds.set(-horizontalMargin, 0, right - left + horizontalMargin, bottom - top);
-                    v.setSystemGestureExclusionRects(List.of(bounds));
-                });
-
-        BrightnessSliderController controller = mToggleSliderFactory.create(this, frame);
-        controller.init();
-        frame.addView(controller.getRootView(), MATCH_PARENT, WRAP_CONTENT);
-
-        mBrightnessController = mBrightnessControllerFactory.create(controller);
-
         Configuration configuration = getResources().getConfiguration();
         int orientation = configuration.orientation;
         int windowWidth = getWindowAvailableWidth();
@@ -165,7 +185,23 @@
             lp.width = windowWidth - horizontalMargin * 2;
         }
 
-        frame.setLayoutParams(lp);
+        container.setLayoutParams(lp);
+        Rect bounds = new Rect();
+        container.addOnLayoutChangeListener(
+                (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
+                    // Exclude this view (and its horizontal margins) from triggering gestures.
+                    // This prevents back gesture from being triggered by dragging close to the
+                    // edge of the slider (0% or 100%).
+                    bounds.set(-horizontalMargin, 0, right - left + horizontalMargin, bottom - top);
+                    v.setSystemGestureExclusionRects(List.of(bounds));
+                });
+    }
+
+    private void setDialogContent(FrameLayout frame) {
+        BrightnessSliderController controller = mToggleSliderFactory.create(this, frame);
+        controller.init();
+        frame.addView(controller.getRootView(), MATCH_PARENT, WRAP_CONTENT);
+        mBrightnessController = mBrightnessControllerFactory.create(controller);
     }
 
     private int getWindowAvailableWidth() {
@@ -181,7 +217,9 @@
     @Override
     protected void onStart() {
         super.onStart();
-        mBrightnessController.registerCallbacks();
+        if (!QSComposeFragment.isEnabled()) {
+            mBrightnessController.registerCallbacks();
+        }
         MetricsLogger.visible(this, MetricsEvent.BRIGHTNESS_DIALOG);
     }
 
@@ -203,7 +241,9 @@
     protected void onStop() {
         super.onStop();
         MetricsLogger.hidden(this, MetricsEvent.BRIGHTNESS_DIALOG);
-        mBrightnessController.unregisterCallbacks();
+        if (!QSComposeFragment.isEnabled()) {
+            mBrightnessController.unregisterCallbacks();
+        }
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderController.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderController.java
index 8703f68..2f7df21 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderController.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderController.java
@@ -30,8 +30,9 @@
 import com.android.settingslib.RestrictedLockUtils;
 import com.android.systemui.Gefingerpoken;
 import com.android.systemui.classifier.Classifier;
+import com.android.systemui.haptics.slider.HapticSlider;
+import com.android.systemui.haptics.slider.HapticSliderPlugin;
 import com.android.systemui.haptics.slider.HapticSliderViewBinder;
-import com.android.systemui.haptics.slider.SeekbarHapticPlugin;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.res.R;
@@ -39,7 +40,6 @@
 import com.android.systemui.statusbar.policy.BrightnessMirrorController;
 import com.android.systemui.util.ViewController;
 import com.android.systemui.util.time.SystemClock;
-
 import com.google.android.msdl.domain.MSDLPlayer;
 
 import javax.inject.Inject;
@@ -65,7 +65,7 @@
     private final FalsingManager mFalsingManager;
     private final UiEventLogger mUiEventLogger;
 
-    private final SeekbarHapticPlugin mBrightnessSliderHapticPlugin;
+    private final HapticSliderPlugin mBrightnessSliderHapticPlugin;
     private final ActivityStarter mActivityStarter;
 
     private final Gefingerpoken mOnInterceptListener = new Gefingerpoken() {
@@ -89,7 +89,7 @@
             BrightnessSliderView brightnessSliderView,
             FalsingManager falsingManager,
             UiEventLogger uiEventLogger,
-            SeekbarHapticPlugin brightnessSliderHapticPlugin,
+            HapticSliderPlugin brightnessSliderHapticPlugin,
             ActivityStarter activityStarter) {
         super(brightnessSliderView);
         mFalsingManager = falsingManager;
@@ -240,7 +240,7 @@
             if (mListener != null) {
                 mListener.onChanged(mTracking, progress, false);
                 if (fromUser) {
-                    mBrightnessSliderHapticPlugin.onProgressChanged(seekBar, progress, fromUser);
+                    mBrightnessSliderHapticPlugin.onProgressChanged(progress, true);
                 }
             }
         }
@@ -251,7 +251,7 @@
             mUiEventLogger.log(BrightnessSliderEvent.BRIGHTNESS_SLIDER_STARTED_TRACKING_TOUCH);
             if (mListener != null) {
                 mListener.onChanged(mTracking, getValue(), false);
-                mBrightnessSliderHapticPlugin.onStartTrackingTouch(seekBar);
+                mBrightnessSliderHapticPlugin.onStartTrackingTouch();
             }
 
             if (mMirrorController != null) {
@@ -266,7 +266,7 @@
             mUiEventLogger.log(BrightnessSliderEvent.BRIGHTNESS_SLIDER_STOPPED_TRACKING_TOUCH);
             if (mListener != null) {
                 mListener.onChanged(mTracking, getValue(), true);
-                mBrightnessSliderHapticPlugin.onStopTrackingTouch(seekBar);
+                mBrightnessSliderHapticPlugin.onStopTrackingTouch();
             }
 
             if (mMirrorController != null) {
@@ -317,10 +317,11 @@
             int layout = getLayout();
             BrightnessSliderView root = (BrightnessSliderView) LayoutInflater.from(context)
                     .inflate(layout, viewRoot, false);
-            SeekbarHapticPlugin plugin = new SeekbarHapticPlugin(
+            HapticSliderPlugin plugin = new HapticSliderPlugin(
                     mVibratorHelper,
                     mMSDLPlayer,
-                    mSystemClock);
+                    mSystemClock,
+                    new HapticSlider.SeekBar(root.requireViewById(R.id.slider)));
             HapticSliderViewBinder.bind(viewRoot, plugin);
             return new BrightnessSliderController(
                     root, mFalsingManager, mUiEventLogger, plugin, mActivityStarter);
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/ComposeDialogComposableProvider.kt b/packages/SystemUI/src/com/android/systemui/settings/brightness/ComposeDialogComposableProvider.kt
new file mode 100644
index 0000000..dde2ebc
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/ComposeDialogComposableProvider.kt
@@ -0,0 +1,62 @@
+/*
+ * 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.settings.brightness
+
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.ComposeView
+import androidx.compose.ui.platform.ViewCompositionStrategy
+import com.android.compose.theme.PlatformTheme
+import com.android.systemui.brightness.ui.compose.BrightnessSliderContainer
+import com.android.systemui.brightness.ui.viewmodel.BrightnessSliderViewModel
+import com.android.systemui.lifecycle.rememberViewModel
+import com.android.systemui.qs.ui.composable.QuickSettingsShade
+
+object ComposeDialogComposableProvider {
+
+    fun setComposableBrightness(composeView: ComposeView, content: ComposableProvider) {
+        composeView.apply {
+            setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
+            setContent { PlatformTheme { content.ProvideComposableContent() } }
+        }
+    }
+}
+
+@Composable
+private fun BrightnessSliderForDialog(
+    brightnessSliderViewModelFactory: BrightnessSliderViewModel.Factory
+) {
+    val viewModel =
+        rememberViewModel(traceName = "BrightnessDialog.viewModel") {
+            brightnessSliderViewModelFactory.create(false)
+        }
+    BrightnessSliderContainer(
+        viewModel = viewModel,
+        Modifier.fillMaxWidth().height(QuickSettingsShade.Dimensions.BrightnessSliderHeight),
+    )
+}
+
+class ComposableProvider(
+    private val brightnessSliderViewModelFactory: BrightnessSliderViewModel.Factory
+) {
+    @Composable
+    fun ProvideComposableContent() {
+        BrightnessSliderForDialog(brightnessSliderViewModelFactory)
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/domain/interactor/BrightnessMirrorShowingInteractor.kt b/packages/SystemUI/src/com/android/systemui/settings/brightness/domain/interactor/BrightnessMirrorShowingInteractor.kt
index ef6e72f..de068a0 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/brightness/domain/interactor/BrightnessMirrorShowingInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/domain/interactor/BrightnessMirrorShowingInteractor.kt
@@ -23,9 +23,12 @@
 @SysUISingleton
 class BrightnessMirrorShowingInteractor
 @Inject
-constructor(
-    private val brightnessMirrorShowingRepository: BrightnessMirrorShowingRepository,
-) {
+constructor(private val brightnessMirrorShowingRepository: BrightnessMirrorShowingRepository) {
+    /**
+     * Whether a brightness mirror is showing (either as a compose overlay or as a separate mirror).
+     *
+     * This can be used to determine whether other views/composables have to be hidden.
+     */
     val isShowing = brightnessMirrorShowingRepository.isShowing
 
     fun setMirrorShowing(showing: Boolean) {
diff --git a/packages/SystemUI/src/com/android/systemui/shade/LargeScreenHeaderHelper.kt b/packages/SystemUI/src/com/android/systemui/shade/LargeScreenHeaderHelper.kt
index c74f038..38de17e 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/LargeScreenHeaderHelper.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/LargeScreenHeaderHelper.kt
@@ -22,7 +22,7 @@
 import javax.inject.Inject
 import kotlin.math.max
 
-class LargeScreenHeaderHelper @Inject constructor(private val context: Context) {
+class LargeScreenHeaderHelper @Inject constructor(@ShadeDisplayAware private val context: Context) {
 
     fun getLargeScreenHeaderHeight(): Int = getLargeScreenHeaderHeight(context)
 
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelUnfoldAnimationController.kt b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelUnfoldAnimationController.kt
index f463cb5..6e63446 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelUnfoldAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelUnfoldAnimationController.kt
@@ -34,7 +34,7 @@
 class NotificationPanelUnfoldAnimationController
 @Inject
 constructor(
-    private val context: Context,
+    @ShadeDisplayAware private val context: Context,
     statusBarStateController: StatusBarStateController,
     progressProvider: NaturalRotationUnfoldProgressProvider,
 ) {
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index 67d162b..1b281b1 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -164,9 +164,11 @@
 import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
 import com.android.systemui.power.domain.interactor.PowerInteractor;
 import com.android.systemui.power.shared.model.WakefulnessModel;
+import com.android.systemui.qs.flags.QSComposeFragment;
 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.settings.brightness.domain.interactor.BrightnessMirrorShowingInteractor;
 import com.android.systemui.shade.data.repository.FlingInfo;
 import com.android.systemui.shade.data.repository.ShadeRepository;
 import com.android.systemui.shade.domain.interactor.ShadeAnimationInteractor;
@@ -664,11 +666,12 @@
             };
 
     private final ActivityStarter mActivityStarter;
+    private final BrightnessMirrorShowingInteractor mBrightnessMirrorShowingInteractor;
 
     @Inject
     public NotificationPanelViewController(NotificationPanelView view,
             @Main Handler handler,
-            LayoutInflater layoutInflater,
+            @ShadeDisplayAware LayoutInflater layoutInflater,
             FeatureFlags featureFlags,
             NotificationWakeUpCoordinator coordinator,
             PulseExpansionHandler pulseExpansionHandler,
@@ -757,7 +760,8 @@
             PowerInteractor powerInteractor,
             KeyguardClockPositionAlgorithm keyguardClockPositionAlgorithm,
             NaturalScrollingSettingObserver naturalScrollingSettingObserver,
-            MSDLPlayer msdlPlayer) {
+            MSDLPlayer msdlPlayer,
+            BrightnessMirrorShowingInteractor brightnessMirrorShowingInteractor) {
         SceneContainerFlag.assertInLegacyMode();
         keyguardStateController.addCallback(new KeyguardStateController.Callback() {
             @Override
@@ -944,6 +948,7 @@
                 },
                 mFalsingManager);
         mActivityStarter = activityStarter;
+        mBrightnessMirrorShowingInteractor = brightnessMirrorShowingInteractor;
         onFinishInflate();
         keyguardUnlockAnimationController.addKeyguardUnlockAnimationListener(
                 new KeyguardUnlockAnimationController.KeyguardUnlockAnimationListener() {
@@ -1185,6 +1190,12 @@
                     }
                 },
                 mMainDispatcher);
+        if (QSComposeFragment.isEnabled()) {
+            collectFlow(mView,
+                    mBrightnessMirrorShowingInteractor.isShowing(),
+                    isShowing -> setAlpha(isShowing ? 0 : 255, true)
+            );
+        }
     }
 
     @VisibleForTesting
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
index 5b6696b..24dba59 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
@@ -148,7 +148,7 @@
 
     @Inject
     public NotificationShadeWindowControllerImpl(
-            Context context,
+            @ShadeDisplayAware Context context,
             WindowRootViewComponent.Factory windowRootViewComponentFactory,
             ViewCaptureAwareWindowManager viewCaptureAwareWindowManager,
             IActivityManager activityManager,
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
index 365666d..be2bf82 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
@@ -54,8 +54,10 @@
 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.qs.flags.QSComposeFragment;
 import com.android.systemui.res.R;
 import com.android.systemui.scene.shared.flag.SceneContainerFlag;
+import com.android.systemui.settings.brightness.domain.interactor.BrightnessMirrorShowingInteractor;
 import com.android.systemui.shade.domain.interactor.PanelExpansionInteractor;
 import com.android.systemui.shade.shared.flag.ShadeWindowGoesAround;
 import com.android.systemui.shared.animation.DisableSubpixelTextTransitionListener;
@@ -191,7 +193,8 @@
             PrimaryBouncerInteractor primaryBouncerInteractor,
             AlternateBouncerInteractor alternateBouncerInteractor,
             BouncerViewBinder bouncerViewBinder,
-            @ShadeDisplayAware ConfigurationForwarder configurationForwarder) {
+            @ShadeDisplayAware ConfigurationForwarder configurationForwarder,
+            BrightnessMirrorShowingInteractor brightnessMirrorShowingInteractor) {
         mLockscreenShadeTransitionController = transitionController;
         mFalsingCollector = falsingCollector;
         mStatusBarStateController = statusBarStateController;
@@ -232,6 +235,11 @@
                 mView,
                 notificationLaunchAnimationInteractor.isLaunchAnimationRunning(),
                 this::setExpandAnimationRunning);
+        if (QSComposeFragment.isEnabled()) {
+            collectFlow(mView,
+                    brightnessMirrorShowingInteractor.isShowing(),
+                    this::setBrightnessMirrorShowingForDepth);
+        }
 
         var keyguardUnfoldTransition = unfoldComponent.map(
                 SysUIUnfoldComponent::getKeyguardUnfoldTransition);
@@ -703,6 +711,10 @@
         }
     }
 
+    private void setBrightnessMirrorShowingForDepth(boolean showing) {
+        mDepthController.setBrightnessMirrorVisible(showing);
+    }
+
     @Override
     public void dump(PrintWriter pw, String[] args) {
         pw.print("  mExpandingBelowNotch=");
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt b/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt
index 437d32d..7a18d7c 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt
@@ -27,6 +27,7 @@
 import androidx.constraintlayout.widget.ConstraintSet.START
 import androidx.constraintlayout.widget.ConstraintSet.TOP
 import androidx.lifecycle.lifecycleScope
+import com.android.app.tracing.coroutines.launchTraced as launch
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.fragments.FragmentService
@@ -49,7 +50,6 @@
 import java.util.function.Consumer
 import javax.inject.Inject
 import kotlin.reflect.KMutableProperty0
-import com.android.app.tracing.coroutines.launchTraced as launch
 
 @VisibleForTesting internal const val INSET_DEBOUNCE_MILLIS = 500L
 
@@ -334,7 +334,7 @@
 private data class Paddings(
     val containerPadding: Int,
     val notificationsMargin: Int,
-    val qsContainerPadding: Int
+    val qsContainerPadding: Int,
 )
 
 private fun KMutableProperty0<Int>.setAndReportChange(newValue: Int): Boolean {
diff --git a/packages/SystemUI/src/com/android/systemui/shade/QsBatteryModeController.kt b/packages/SystemUI/src/com/android/systemui/shade/QsBatteryModeController.kt
index 91627d6..7a70966 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/QsBatteryModeController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/QsBatteryModeController.kt
@@ -14,7 +14,7 @@
 class QsBatteryModeController
 @Inject
 constructor(
-    private val context: Context,
+    @ShadeDisplayAware private val context: Context,
     insetsProviderStore: StatusBarContentInsetsProviderStore,
 ) {
 
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeExpandsOnStatusBarLongPress.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeExpandsOnStatusBarLongPress.kt
new file mode 100644
index 0000000..6d8e898
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeExpandsOnStatusBarLongPress.kt
@@ -0,0 +1,61 @@
+/*
+ * 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.shade
+
+import com.android.systemui.Flags
+import com.android.systemui.flags.FlagToken
+import com.android.systemui.flags.RefactorFlagUtils
+
+/** Helper for reading or using the shade expands on status bar long press flag state. */
+@Suppress("NOTHING_TO_INLINE")
+object ShadeExpandsOnStatusBarLongPress {
+    /** The aconfig flag name */
+    const val FLAG_NAME = Flags.FLAG_SHADE_EXPANDS_ON_STATUS_BAR_LONG_PRESS
+
+    /** A token used for dependency declaration */
+    val token: FlagToken
+        get() = FlagToken(FLAG_NAME, isEnabled)
+
+    /** Is the refactor enabled */
+    @JvmStatic
+    inline val isEnabled
+        get() = Flags.shadeExpandsOnStatusBarLongPress()
+
+    /**
+     * Called to ensure code is only run when the flag is enabled. This protects users from the
+     * unintended behaviors caused by accidentally running new logic, while also crashing on an eng
+     * build to ensure that the refactor author catches issues in testing.
+     */
+    @JvmStatic
+    inline fun isUnexpectedlyInLegacyMode() =
+        RefactorFlagUtils.isUnexpectedlyInLegacyMode(isEnabled, FLAG_NAME)
+
+    /**
+     * Called to ensure code is only run when the flag is enabled. This will throw an exception if
+     * the flag is not enabled to ensure that the refactor author catches issues in testing.
+     * Caution!! Using this check incorrectly will cause crashes in nextfood builds!
+     */
+    @JvmStatic
+    inline fun assertInNewMode() = RefactorFlagUtils.assertInNewMode(isEnabled, FLAG_NAME)
+
+    /**
+     * Called to ensure code is only run when the flag is disabled. This will throw an exception if
+     * the flag is enabled to ensure that the refactor author catches issues in testing.
+     */
+    @JvmStatic
+    inline fun assertInLegacyMode() = RefactorFlagUtils.assertInLegacyMode(isEnabled, FLAG_NAME)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewProviderModule.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewProviderModule.kt
index 1918094..15b2270 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewProviderModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewProviderModule.kt
@@ -83,7 +83,7 @@
         @Provides
         @SysUISingleton
         fun providesWindowRootView(
-            layoutInflater: LayoutInflater,
+            @ShadeDisplayAware layoutInflater: LayoutInflater,
             viewModelFactory: SceneContainerViewModel.Factory,
             containerConfigProvider: Provider<SceneContainerConfig>,
             scenesProvider: Provider<Set<@JvmSuppressWildcards Scene>>,
@@ -156,7 +156,7 @@
         @Provides
         fun providesKeyguardBottomAreaView(
             npv: NotificationPanelView,
-            layoutInflater: LayoutInflater,
+            @ShadeDisplayAware layoutInflater: LayoutInflater,
         ): KeyguardBottomAreaView {
             return layoutInflater.inflate(R.layout.keyguard_bottom_area, npv, false)
                 as KeyguardBottomAreaView
diff --git a/packages/SystemUI/src/com/android/systemui/shade/carrier/ShadeCarrierGroupController.java b/packages/SystemUI/src/com/android/systemui/shade/carrier/ShadeCarrierGroupController.java
index a171d33..4b8cc00 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/carrier/ShadeCarrierGroupController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/carrier/ShadeCarrierGroupController.java
@@ -44,6 +44,7 @@
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.res.R;
+import com.android.systemui.shade.ShadeDisplayAware;
 import com.android.systemui.statusbar.connectivity.MobileDataIndicators;
 import com.android.systemui.statusbar.connectivity.NetworkController;
 import com.android.systemui.statusbar.connectivity.SignalCallback;
@@ -153,7 +154,7 @@
             ShadeCarrierGroupControllerLogger logger,
             NetworkController networkController,
             CarrierTextManager.Builder carrierTextManagerBuilder,
-            Context context,
+            @ShadeDisplayAware Context context,
             CarrierConfigTracker carrierConfigTracker,
             SlotIndexResolver slotIndexResolver,
             MobileUiAdapter mobileUiAdapter,
@@ -497,7 +498,7 @@
                 ShadeCarrierGroupControllerLogger logger,
                 NetworkController networkController,
                 CarrierTextManager.Builder carrierTextControllerBuilder,
-                Context context,
+                @ShadeDisplayAware Context context,
                 CarrierConfigTracker carrierConfigTracker,
                 SlotIndexResolver slotIndexResolver,
                 MobileUiAdapter mobileUiAdapter,
diff --git a/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeRepository.kt b/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeRepository.kt
index 193056c..5629938 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeRepository.kt
@@ -182,7 +182,7 @@
 
 /** Business logic for shade interactions */
 @SysUISingleton
-class ShadeRepositoryImpl @Inject constructor(@Application applicationContext: Context) :
+class ShadeRepositoryImpl @Inject constructor() :
     ShadeRepository {
     private val _qsExpansion = MutableStateFlow(0f)
     @Deprecated("Use ShadeInteractor.qsExpansion instead")
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/startable/ShadeStartable.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/startable/ShadeStartable.kt
index 6c44c73..e5d08a0 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/domain/startable/ShadeStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/startable/ShadeStartable.kt
@@ -26,6 +26,7 @@
 import com.android.systemui.log.dagger.ShadeTouchLog
 import com.android.systemui.scene.domain.interactor.SceneInteractor
 import com.android.systemui.scene.shared.flag.SceneContainerFlag
+import com.android.systemui.shade.ShadeDisplayAware
 import com.android.systemui.shade.ShadeExpansionStateManager
 import com.android.systemui.shade.TouchLogger.Companion.logTouchesTo
 import com.android.systemui.shade.data.repository.ShadeRepository
@@ -48,7 +49,7 @@
 @Inject
 constructor(
     @Application private val applicationScope: CoroutineScope,
-    @Application private val applicationContext: Context,
+    @ShadeDisplayAware private val context: Context,
     @ShadeTouchLog private val touchLog: LogBuffer,
     private val configurationRepository: ConfigurationRepository,
     private val shadeRepository: ShadeRepository,
@@ -94,7 +95,7 @@
                 // Force initial collection.
                 .onStart { emit(Unit) }
                 .collect {
-                    val resources = applicationContext.resources
+                    val resources = context.resources
                     // The configuration for 'shouldUseSplitNotificationShade' dictates the width of
                     // the shade in both split-shade and dual-shade modes.
                     shadeRepository.setShadeLayoutWide(
diff --git a/packages/SystemUI/src/com/android/systemui/shade/transition/LargeScreenShadeInterpolatorImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/transition/LargeScreenShadeInterpolatorImpl.kt
index 823742a..a5caa09 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/transition/LargeScreenShadeInterpolatorImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/transition/LargeScreenShadeInterpolatorImpl.kt
@@ -30,7 +30,7 @@
 @Inject
 internal constructor(
     @ShadeDisplayAware configurationController: ConfigurationController,
-    private val context: Context,
+    @ShadeDisplayAware private val context: Context,
     private val splitShadeInterpolator: SplitShadeInterpolator,
     private val portraitShadeInterpolator: LargeScreenPortraitShadeInterpolator,
     private val splitShadeStateController: SplitShadeStateController,
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModel.kt b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModel.kt
index 5be5ccc..45516aa 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModel.kt
@@ -30,6 +30,7 @@
 import com.android.systemui.privacy.PrivacyItem
 import com.android.systemui.res.R
 import com.android.systemui.scene.shared.model.TransitionKeys.SlightlyFasterShadeCollapse
+import com.android.systemui.shade.ShadeDisplayAware
 import com.android.systemui.shade.domain.interactor.PrivacyChipInteractor
 import com.android.systemui.shade.domain.interactor.ShadeHeaderClockInteractor
 import com.android.systemui.shade.domain.interactor.ShadeInteractor
@@ -53,7 +54,7 @@
 class ShadeHeaderViewModel
 @AssistedInject
 constructor(
-    context: Context,
+    @ShadeDisplayAware context: Context,
     private val activityStarter: ActivityStarter,
     private val shadeInteractor: ShadeInteractor,
     private val mobileIconsInteractor: MobileIconsInteractor,
diff --git a/packages/SystemUI/src/com/android/systemui/smartspace/config/BcSmartspaceConfigProvider.kt b/packages/SystemUI/src/com/android/systemui/smartspace/config/BcSmartspaceConfigProvider.kt
index 0e1bf72..5db1dcb 100644
--- a/packages/SystemUI/src/com/android/systemui/smartspace/config/BcSmartspaceConfigProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/smartspace/config/BcSmartspaceConfigProvider.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.smartspace.config
 
+import com.android.systemui.Flags.smartspaceSwipeEventLogging
 import com.android.systemui.Flags.smartspaceViewpager2
 import com.android.systemui.flags.FeatureFlags
 import com.android.systemui.plugins.BcSmartspaceConfigPlugin
@@ -27,4 +28,7 @@
 
     override val isViewPager2Enabled: Boolean
         get() = smartspaceViewpager2()
+
+    override val isSwipeEventLoggingEnabled: Boolean
+        get() = smartspaceSwipeEventLogging()
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/viewmodel/CastToOtherDeviceChipViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/viewmodel/CastToOtherDeviceChipViewModel.kt
index d4ad6ee..1107206 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/viewmodel/CastToOtherDeviceChipViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/viewmodel/CastToOtherDeviceChipViewModel.kt
@@ -68,23 +68,24 @@
     private val endMediaProjectionDialogHelper: EndMediaProjectionDialogHelper,
     @StatusBarChipsLog private val logger: LogBuffer,
 ) : OngoingActivityChipViewModel {
-    /**
-     * The cast chip to show, based only on MediaProjection API events.
-     *
-     * This chip will only be [OngoingActivityChipModel.Shown] when the user is casting their
-     * *screen*. If the user is only casting audio, this chip will be
-     * [OngoingActivityChipModel.Hidden].
-     */
+    /** The cast chip to show, based only on MediaProjection API events. */
     private val projectionChip: StateFlow<OngoingActivityChipModel> =
         mediaProjectionChipInteractor.projection
             .map { projectionModel ->
                 when (projectionModel) {
                     is ProjectionChipModel.NotProjecting -> OngoingActivityChipModel.Hidden()
                     is ProjectionChipModel.Projecting -> {
-                        if (projectionModel.type != ProjectionChipModel.Type.CAST_TO_OTHER_DEVICE) {
-                            OngoingActivityChipModel.Hidden()
-                        } else {
-                            createCastScreenToOtherDeviceChip(projectionModel)
+                        when (projectionModel.receiver) {
+                            ProjectionChipModel.Receiver.CastToOtherDevice -> {
+                                when (projectionModel.contentType) {
+                                    ProjectionChipModel.ContentType.Screen ->
+                                        createCastScreenToOtherDeviceChip(projectionModel)
+                                    ProjectionChipModel.ContentType.Audio ->
+                                        createIconOnlyCastChip(deviceName = null)
+                                }
+                            }
+                            ProjectionChipModel.Receiver.ShareToApp ->
+                                OngoingActivityChipModel.Hidden()
                         }
                     }
                 }
@@ -98,9 +99,9 @@
      * This chip will be [OngoingActivityChipModel.Shown] when the user is casting their screen *or*
      * their audio.
      *
-     * The MediaProjection APIs are not invoked for casting *only audio* to another device because
-     * MediaProjection is only concerned with *screen* sharing (see b/342169876). We listen to
-     * MediaRouter APIs here to cover audio-only casting.
+     * The MediaProjection APIs are typically not invoked for casting *only audio* to another device
+     * because MediaProjection is only concerned with *screen* sharing (see b/342169876). We listen
+     * to MediaRouter APIs here to cover audio-only casting.
      *
      * Note that this means we will start showing the cast chip before the casting actually starts,
      * for **both** audio-only casting and screen casting. MediaRouter is aware of all
@@ -139,7 +140,7 @@
                         str1 = projection.logName
                         str2 = router.logName
                     },
-                    { "projectionChip=$str1 > routerChip=$str2" }
+                    { "projectionChip=$str1 > routerChip=$str2" },
                 )
 
                 // A consequence of b/269975671 is that MediaRouter and MediaProjection APIs fire at
@@ -186,7 +187,7 @@
     }
 
     private fun createCastScreenToOtherDeviceChip(
-        state: ProjectionChipModel.Projecting,
+        state: ProjectionChipModel.Projecting
     ): OngoingActivityChipModel.Shown {
         return OngoingActivityChipModel.Shown.Timer(
             icon =
@@ -195,7 +196,7 @@
                         CAST_TO_OTHER_DEVICE_ICON,
                         // This string is "Casting screen"
                         ContentDescription.Resource(
-                            R.string.cast_screen_to_other_device_chip_accessibility_label,
+                            R.string.cast_screen_to_other_device_chip_accessibility_label
                         ),
                     )
                 ),
@@ -236,9 +237,7 @@
         )
     }
 
-    private fun createCastScreenToOtherDeviceDialogDelegate(
-        state: ProjectionChipModel.Projecting,
-    ) =
+    private fun createCastScreenToOtherDeviceDialogDelegate(state: ProjectionChipModel.Projecting) =
         EndCastScreenToOtherDeviceDialogDelegate(
             endMediaProjectionDialogHelper,
             context,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractor.kt
index 8abe1d3..27b2465 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractor.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.statusbar.chips.mediaprojection.domain.interactor
 
 import android.content.pm.PackageManager
+import com.android.systemui.Flags
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.log.LogBuffer
@@ -59,23 +60,43 @@
                         ProjectionChipModel.NotProjecting
                     }
                     is MediaProjectionState.Projecting -> {
-                        val type =
+                        val receiver =
                             if (packageHasCastingCapabilities(packageManager, state.hostPackage)) {
-                                ProjectionChipModel.Type.CAST_TO_OTHER_DEVICE
+                                ProjectionChipModel.Receiver.CastToOtherDevice
                             } else {
-                                ProjectionChipModel.Type.SHARE_TO_APP
+                                ProjectionChipModel.Receiver.ShareToApp
                             }
+                        val contentType =
+                            if (Flags.statusBarShowAudioOnlyProjectionChip()) {
+                                when (state) {
+                                    is MediaProjectionState.Projecting.EntireScreen,
+                                    is MediaProjectionState.Projecting.SingleTask ->
+                                        ProjectionChipModel.ContentType.Screen
+                                    is MediaProjectionState.Projecting.NoScreen ->
+                                        ProjectionChipModel.ContentType.Audio
+                                }
+                            } else {
+                                ProjectionChipModel.ContentType.Screen
+                            }
+
                         logger.log(
                             TAG,
                             LogLevel.INFO,
                             {
-                                str1 = type.name
-                                str2 = state.hostPackage
-                                str3 = state.hostDeviceName
+                                bool1 = receiver == ProjectionChipModel.Receiver.CastToOtherDevice
+                                bool2 = contentType == ProjectionChipModel.ContentType.Screen
+                                str1 = state.hostPackage
+                                str2 = state.hostDeviceName
                             },
-                            { "State: Projecting(type=$str1 hostPackage=$str2 hostDevice=$str3)" }
+                            {
+                                "State: Projecting(" +
+                                    "receiver=${if (bool1) "CastToOtherDevice" else "ShareToApp"} " +
+                                    "contentType=${if (bool2) "Screen" else "Audio"} " +
+                                    "hostPackage=$str1 " +
+                                    "hostDevice=$str2)"
+                            },
                         )
-                        ProjectionChipModel.Projecting(type, state)
+                        ProjectionChipModel.Projecting(receiver, contentType, state)
                     }
                 }
             }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/mediaprojection/domain/model/ProjectionChipModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/mediaprojection/domain/model/ProjectionChipModel.kt
index 85682f5..c6283e9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/mediaprojection/domain/model/ProjectionChipModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/mediaprojection/domain/model/ProjectionChipModel.kt
@@ -28,16 +28,22 @@
 
     /** Media is currently being projected. */
     data class Projecting(
-        val type: Type,
+        val receiver: Receiver,
+        val contentType: ContentType,
         val projectionState: MediaProjectionState.Projecting,
     ) : ProjectionChipModel()
 
-    enum class Type {
-        /**
-         * This projection is sharing your phone screen content to another app on the same device.
-         */
-        SHARE_TO_APP,
-        /** This projection is sharing your phone screen content to a different device. */
-        CAST_TO_OTHER_DEVICE,
+    enum class Receiver {
+        /** This projection is sharing to another app on the same device. */
+        ShareToApp,
+        /** This projection is sharing to a different device. */
+        CastToOtherDevice,
+    }
+
+    enum class ContentType {
+        /** This projection is sharing your device's screen content. */
+        Screen,
+        /** This projection is sharing your device's audio (but *not* screen). */
+        Audio,
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/domain/interactor/StatusBarNotificationChipsInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/domain/interactor/StatusBarNotificationChipsInteractor.kt
new file mode 100644
index 0000000..9e09671
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/domain/interactor/StatusBarNotificationChipsInteractor.kt
@@ -0,0 +1,48 @@
+/*
+ * 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.chips.notification.domain.interactor
+
+import android.annotation.SuppressLint
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips
+import javax.inject.Inject
+import kotlinx.coroutines.flow.MutableSharedFlow
+import kotlinx.coroutines.flow.SharedFlow
+import kotlinx.coroutines.flow.asSharedFlow
+
+/** An interactor for the notification chips shown in the status bar. */
+@SysUISingleton
+class StatusBarNotificationChipsInteractor @Inject constructor() {
+
+    // Each chip tap is an individual event, *not* a state, which is why we're using SharedFlow not
+    // StateFlow. There shouldn't be multiple updates per frame, which should avoid performance
+    // problems.
+    @SuppressLint("SharedFlowCreation")
+    private val _promotedNotificationChipTapEvent = MutableSharedFlow<String>()
+
+    /**
+     * SharedFlow that emits each time a promoted notification's status bar chip is tapped. The
+     * emitted value is the promoted notification's key.
+     */
+    val promotedNotificationChipTapEvent: SharedFlow<String> =
+        _promotedNotificationChipTapEvent.asSharedFlow()
+
+    suspend fun onPromotedNotificationChipTapped(key: String) {
+        StatusBarNotifChips.assertInNewMode()
+        _promotedNotificationChipTapEvent.emit(key)
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModel.kt
index 6ae9263..c8d3f33 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModel.kt
@@ -16,20 +16,30 @@
 
 package com.android.systemui.statusbar.chips.notification.ui.viewmodel
 
+import android.view.View
 import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.statusbar.chips.notification.domain.interactor.StatusBarNotificationChipsInteractor
+import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips
 import com.android.systemui.statusbar.chips.ui.model.ColorsModel
 import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
 import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor
 import com.android.systemui.statusbar.notification.shared.ActiveNotificationModel
 import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.launch
 
 /** A view model for status bar chips for promoted ongoing notifications. */
 @SysUISingleton
 class NotifChipsViewModel
 @Inject
-constructor(activeNotificationsInteractor: ActiveNotificationsInteractor) {
+constructor(
+    @Application private val applicationScope: CoroutineScope,
+    activeNotificationsInteractor: ActiveNotificationsInteractor,
+    private val notifChipsInteractor: StatusBarNotificationChipsInteractor,
+) {
     /**
      * A flow modeling the notification chips that should be shown. Emits an empty list if there are
      * no notifications that should show a status bar chip.
@@ -44,13 +54,20 @@
      * notification has invalid data such that it can't be displayed as a chip.
      */
     private fun ActiveNotificationModel.toChipModel(): OngoingActivityChipModel.Shown? {
+        StatusBarNotifChips.assertInNewMode()
         // TODO(b/364653005): Log error if there's no icon view.
         val rawIcon = this.statusBarChipIconView ?: return null
         val icon = OngoingActivityChipModel.ChipIcon.StatusBarView(rawIcon)
         // TODO(b/364653005): Use the notification color if applicable.
         val colors = ColorsModel.Themed
-        // TODO(b/364653005): When the chip is clicked, show the HUN.
-        val onClickListener = null
+        val onClickListener =
+            View.OnClickListener {
+                // The notification pipeline needs everything to run on the main thread, so keep
+                // this event on the main thread.
+                applicationScope.launch {
+                    notifChipsInteractor.onPromotedNotificationChipTapped(this@toChipModel.key)
+                }
+            }
         return OngoingActivityChipModel.Shown.ShortTimeDelta(
             icon,
             colors,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/sharetoapp/ui/view/EndGenericShareToAppDialogDelegate.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/sharetoapp/ui/view/EndGenericShareToAppDialogDelegate.kt
new file mode 100644
index 0000000..8ec0567
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/sharetoapp/ui/view/EndGenericShareToAppDialogDelegate.kt
@@ -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.systemui.statusbar.chips.sharetoapp.ui.view
+
+import android.content.Context
+import android.os.Bundle
+import com.android.systemui.res.R
+import com.android.systemui.statusbar.chips.mediaprojection.ui.view.EndMediaProjectionDialogHelper
+import com.android.systemui.statusbar.chips.sharetoapp.ui.viewmodel.ShareToAppChipViewModel.Companion.SHARE_TO_APP_ICON
+import com.android.systemui.statusbar.phone.SystemUIDialog
+
+/**
+ * A dialog that lets the user stop an ongoing share-to-app event. The user could be sharing their
+ * screen or just sharing their audio. This dialog uses generic strings to handle both cases well.
+ */
+class EndGenericShareToAppDialogDelegate(
+    private val endMediaProjectionDialogHelper: EndMediaProjectionDialogHelper,
+    private val context: Context,
+    private val stopAction: () -> Unit,
+) : SystemUIDialog.Delegate {
+    override fun createDialog(): SystemUIDialog {
+        return endMediaProjectionDialogHelper.createDialog(this)
+    }
+
+    override fun beforeCreate(dialog: SystemUIDialog, savedInstanceState: Bundle?) {
+        val message = context.getString(R.string.share_to_app_stop_dialog_message_generic)
+        with(dialog) {
+            setIcon(SHARE_TO_APP_ICON)
+            setTitle(R.string.share_to_app_stop_dialog_title_generic)
+            setMessage(message)
+            // No custom on-click, because the dialog will automatically be dismissed when the
+            // button is clicked anyway.
+            setNegativeButton(R.string.close_dialog_button, /* onClick= */ null)
+            setPositiveButton(
+                R.string.share_to_app_stop_dialog_button,
+                endMediaProjectionDialogHelper.wrapStopAction(stopAction),
+            )
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/sharetoapp/ui/view/EndShareToAppDialogDelegate.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/sharetoapp/ui/view/EndShareScreenToAppDialogDelegate.kt
similarity index 97%
rename from packages/SystemUI/src/com/android/systemui/statusbar/chips/sharetoapp/ui/view/EndShareToAppDialogDelegate.kt
rename to packages/SystemUI/src/com/android/systemui/statusbar/chips/sharetoapp/ui/view/EndShareScreenToAppDialogDelegate.kt
index d10bd77..053016e3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/sharetoapp/ui/view/EndShareToAppDialogDelegate.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/sharetoapp/ui/view/EndShareScreenToAppDialogDelegate.kt
@@ -26,7 +26,7 @@
 import com.android.systemui.statusbar.phone.SystemUIDialog
 
 /** A dialog that lets the user stop an ongoing share-screen-to-app event. */
-class EndShareToAppDialogDelegate(
+class EndShareScreenToAppDialogDelegate(
     private val endMediaProjectionDialogHelper: EndMediaProjectionDialogHelper,
     private val context: Context,
     private val stopAction: () -> Unit,
@@ -71,7 +71,7 @@
             if (hostAppName != null) {
                 context.getString(
                     R.string.share_to_app_stop_dialog_message_entire_screen_with_host_app,
-                    hostAppName
+                    hostAppName,
                 )
             } else {
                 context.getString(R.string.share_to_app_stop_dialog_message_entire_screen)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/sharetoapp/ui/viewmodel/ShareToAppChipViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/sharetoapp/ui/viewmodel/ShareToAppChipViewModel.kt
index d99a916..11d077f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/sharetoapp/ui/viewmodel/ShareToAppChipViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/sharetoapp/ui/viewmodel/ShareToAppChipViewModel.kt
@@ -32,7 +32,8 @@
 import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.MediaProjectionChipInteractor
 import com.android.systemui.statusbar.chips.mediaprojection.domain.model.ProjectionChipModel
 import com.android.systemui.statusbar.chips.mediaprojection.ui.view.EndMediaProjectionDialogHelper
-import com.android.systemui.statusbar.chips.sharetoapp.ui.view.EndShareToAppDialogDelegate
+import com.android.systemui.statusbar.chips.sharetoapp.ui.view.EndGenericShareToAppDialogDelegate
+import com.android.systemui.statusbar.chips.sharetoapp.ui.view.EndShareScreenToAppDialogDelegate
 import com.android.systemui.statusbar.chips.ui.model.ColorsModel
 import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
 import com.android.systemui.statusbar.chips.ui.viewmodel.ChipTransitionHelper
@@ -68,10 +69,17 @@
                 when (projectionModel) {
                     is ProjectionChipModel.NotProjecting -> OngoingActivityChipModel.Hidden()
                     is ProjectionChipModel.Projecting -> {
-                        if (projectionModel.type != ProjectionChipModel.Type.SHARE_TO_APP) {
-                            OngoingActivityChipModel.Hidden()
-                        } else {
-                            createShareToAppChip(projectionModel)
+                        when (projectionModel.receiver) {
+                            ProjectionChipModel.Receiver.ShareToApp -> {
+                                when (projectionModel.contentType) {
+                                    ProjectionChipModel.ContentType.Screen ->
+                                        createShareScreenToAppChip(projectionModel)
+                                    ProjectionChipModel.ContentType.Audio ->
+                                        createIconOnlyShareToAppChip()
+                                }
+                            }
+                            ProjectionChipModel.Receiver.CastToOtherDevice ->
+                                OngoingActivityChipModel.Hidden()
                         }
                     }
                 }
@@ -105,8 +113,8 @@
         mediaProjectionChipInteractor.stopProjecting()
     }
 
-    private fun createShareToAppChip(
-        state: ProjectionChipModel.Projecting,
+    private fun createShareScreenToAppChip(
+        state: ProjectionChipModel.Projecting
     ): OngoingActivityChipModel.Shown {
         return OngoingActivityChipModel.Shown.Timer(
             icon =
@@ -120,11 +128,33 @@
             // TODO(b/332662551): Maybe use a MediaProjection API to fetch this time.
             startTimeMs = systemClock.elapsedRealtime(),
             createDialogLaunchOnClickListener(
-                createShareToAppDialogDelegate(state),
+                createShareScreenToAppDialogDelegate(state),
+                dialogTransitionAnimator,
+                DialogCuj(Cuj.CUJ_STATUS_BAR_LAUNCH_DIALOG_FROM_CHIP, tag = "Share to app"),
+                logger,
+                TAG,
+            ),
+        )
+    }
+
+    private fun createIconOnlyShareToAppChip(): OngoingActivityChipModel.Shown {
+        return OngoingActivityChipModel.Shown.IconOnly(
+            icon =
+                OngoingActivityChipModel.ChipIcon.SingleColorIcon(
+                    Icon.Resource(
+                        SHARE_TO_APP_ICON,
+                        ContentDescription.Resource(
+                            R.string.share_to_app_chip_accessibility_label_generic
+                        ),
+                    )
+                ),
+            colors = ColorsModel.Red,
+            createDialogLaunchOnClickListener(
+                createGenericShareToAppDialogDelegate(),
                 dialogTransitionAnimator,
                 DialogCuj(
                     Cuj.CUJ_STATUS_BAR_LAUNCH_DIALOG_FROM_CHIP,
-                    tag = "Share to app",
+                    tag = "Share to app audio only",
                 ),
                 logger,
                 TAG,
@@ -132,14 +162,21 @@
         )
     }
 
-    private fun createShareToAppDialogDelegate(state: ProjectionChipModel.Projecting) =
-        EndShareToAppDialogDelegate(
+    private fun createShareScreenToAppDialogDelegate(state: ProjectionChipModel.Projecting) =
+        EndShareScreenToAppDialogDelegate(
             endMediaProjectionDialogHelper,
             context,
             stopAction = this::stopProjectingFromDialog,
             state,
         )
 
+    private fun createGenericShareToAppDialogDelegate() =
+        EndGenericShareToAppDialogDelegate(
+            endMediaProjectionDialogHelper,
+            context,
+            stopAction = this::stopProjectingFromDialog,
+        )
+
     companion object {
         @DrawableRes val SHARE_TO_APP_ICON = R.drawable.ic_present_to_all
         private const val TAG = "ShareToAppVM"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesModule.java
index 525f3de..ad22caa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesModule.java
@@ -17,9 +17,6 @@
 package com.android.systemui.statusbar.dagger;
 
 import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.emergency.EmergencyGestureModule;
-import com.android.systemui.statusbar.notification.dagger.NotificationsModule;
-import com.android.systemui.statusbar.notification.row.NotificationRowModule;
 import com.android.systemui.statusbar.phone.CentralSurfaces;
 import com.android.systemui.statusbar.phone.CentralSurfacesImpl;
 import com.android.systemui.statusbar.phone.StatusBarNotificationPresenterModule;
@@ -31,8 +28,7 @@
  * Dagger Module providing {@link CentralSurfacesImpl}.
  */
 @Module(includes = {CentralSurfacesDependenciesModule.class,
-        StatusBarNotificationPresenterModule.class,
-        NotificationsModule.class, NotificationRowModule.class, EmergencyGestureModule.class})
+        StatusBarNotificationPresenterModule.class})
 public interface CentralSurfacesModule {
     /**
      * Provides our instance of CentralSurfaces which is considered optional.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/data/StatusBarDataLayerModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/data/StatusBarDataLayerModule.kt
index 8a850b0..c416bf7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/data/StatusBarDataLayerModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/data/StatusBarDataLayerModule.kt
@@ -16,7 +16,6 @@
 package com.android.systemui.statusbar.data
 
 import com.android.systemui.statusbar.data.repository.KeyguardStatusBarRepositoryModule
-import com.android.systemui.statusbar.data.repository.PrivacyDotViewControllerStoreModule
 import com.android.systemui.statusbar.data.repository.RemoteInputRepositoryModule
 import com.android.systemui.statusbar.data.repository.StatusBarConfigurationControllerModule
 import com.android.systemui.statusbar.data.repository.StatusBarContentInsetsProviderStoreModule
@@ -28,7 +27,6 @@
     includes =
         [
             KeyguardStatusBarRepositoryModule::class,
-            PrivacyDotViewControllerStoreModule::class,
             RemoteInputRepositoryModule::class,
             StatusBarConfigurationControllerModule::class,
             StatusBarContentInsetsProviderStoreModule::class,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotCorner.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotCorner.kt
new file mode 100644
index 0000000..8a6f355
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotCorner.kt
@@ -0,0 +1,61 @@
+/*
+ * 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.events
+
+import android.view.Gravity
+import android.view.Surface
+
+/** Represents a corner on the display for the privacy dot. */
+enum class PrivacyDotCorner(
+    val index: Int,
+    val gravity: Int,
+    val innerGravity: Int,
+    val title: String,
+) {
+    TopLeft(
+        index = 0,
+        gravity = Gravity.TOP or Gravity.LEFT,
+        innerGravity = Gravity.CENTER_VERTICAL or Gravity.RIGHT,
+        title = "TopLeft",
+    ),
+    TopRight(
+        index = 1,
+        gravity = Gravity.TOP or Gravity.RIGHT,
+        innerGravity = Gravity.CENTER_VERTICAL or Gravity.LEFT,
+        title = "TopRight",
+    ),
+    BottomRight(
+        index = 2,
+        gravity = Gravity.BOTTOM or Gravity.RIGHT,
+        innerGravity = Gravity.CENTER_VERTICAL or Gravity.RIGHT,
+        title = "BottomRight",
+    ),
+    BottomLeft(
+        index = 3,
+        gravity = Gravity.BOTTOM or Gravity.LEFT,
+        innerGravity = Gravity.CENTER_VERTICAL or Gravity.LEFT,
+        title = "BottomLeft",
+    ),
+}
+
+fun PrivacyDotCorner.rotatedCorner(@Surface.Rotation rotation: Int): PrivacyDotCorner {
+    var modded = index - rotation
+    if (modded < 0) {
+        modded += 4
+    }
+    return PrivacyDotCorner.entries[modded]
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt
index 914cc50..f7bc23c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt
@@ -20,12 +20,13 @@
 import android.graphics.Point
 import android.graphics.Rect
 import android.util.Log
-import android.view.Gravity
 import android.view.View
 import android.widget.FrameLayout
 import androidx.core.animation.Animator
 import com.android.app.animation.Interpolators
+import com.android.app.tracing.coroutines.launchTraced as launch
 import com.android.internal.annotations.GuardedBy
+import com.android.systemui.ScreenDecorationsThread
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.Main
@@ -36,6 +37,10 @@
 import com.android.systemui.statusbar.StatusBarState.SHADE_LOCKED
 import com.android.systemui.statusbar.core.StatusBarConnectedDisplays
 import com.android.systemui.statusbar.data.repository.StatusBarContentInsetsProviderStore
+import com.android.systemui.statusbar.events.PrivacyDotCorner.BottomLeft
+import com.android.systemui.statusbar.events.PrivacyDotCorner.BottomRight
+import com.android.systemui.statusbar.events.PrivacyDotCorner.TopLeft
+import com.android.systemui.statusbar.events.PrivacyDotCorner.TopRight
 import com.android.systemui.statusbar.phone.StatusBarContentInsetsChangedListener
 import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider
 import com.android.systemui.statusbar.policy.ConfigurationController
@@ -53,7 +58,6 @@
 import dagger.assisted.AssistedInject
 import java.util.concurrent.Executor
 import kotlinx.coroutines.CoroutineScope
-import com.android.app.tracing.coroutines.launchTraced as launch
 
 /**
  * Understands how to keep the persistent privacy dot in the corner of the screen in
@@ -81,10 +85,6 @@
 
     var showingListener: ShowingListener?
 
-    fun setUiExecutor(e: DelayableExecutor)
-
-    fun getUiExecutor(): DelayableExecutor?
-
     @UiThread fun setNewRotation(rot: Int)
 
     @UiThread fun hideDotView(dot: View, animate: Boolean)
@@ -117,6 +117,7 @@
     @Assisted private val contentInsetsProvider: StatusBarContentInsetsProvider,
     private val animationScheduler: SystemStatusAnimationScheduler,
     shadeInteractor: ShadeInteractor?,
+    @ScreenDecorationsThread val uiExecutor: DelayableExecutor,
 ) : PrivacyDotViewController {
     private lateinit var tl: View
     private lateinit var tr: View
@@ -136,9 +137,6 @@
     private val lock = Object()
     private var cancelRunnable: Runnable? = null
 
-    // Privacy dots are created in ScreenDecoration's UiThread, which is not the main thread
-    private var uiExecutor: DelayableExecutor? = null
-
     private val views: Sequence<View>
         get() = if (!this::tl.isInitialized) sequenceOf() else sequenceOf(tl, tr, br, bl)
 
@@ -155,7 +153,7 @@
     private val configurationListener =
         object : ConfigurationController.ConfigurationListener {
             override fun onLayoutDirectionChanged(isRtl: Boolean) {
-                uiExecutor?.execute {
+                uiExecutor.execute {
                     // If rtl changed, hide all dots until the next state resolves
                     setCornerVisibilities(View.INVISIBLE)
 
@@ -198,14 +196,6 @@
         stateController.removeCallback(statusBarStateListener)
     }
 
-    override fun setUiExecutor(e: DelayableExecutor) {
-        uiExecutor = e
-    }
-
-    override fun getUiExecutor(): DelayableExecutor? {
-        return uiExecutor
-    }
-
     @UiThread
     override fun setNewRotation(rot: Int) {
         dlog("updateRotation: $rot")
@@ -222,8 +212,8 @@
         // If we rotated, hide all dotes until the next state resolves
         setCornerVisibilities(View.INVISIBLE)
 
-        val newCorner = selectDesignatedCorner(rot, isRtl)
-        val index = newCorner.cornerIndex()
+        val newCornerView = selectDesignatedCorner(rot, isRtl)
+        val corner = newCornerView.corner()
         val paddingTop = contentInsetsProvider.getStatusBarPaddingTop(rot)
 
         synchronized(lock) {
@@ -231,8 +221,8 @@
                 nextViewState.copy(
                     rotation = rot,
                     paddingTop = paddingTop,
-                    designatedCorner = newCorner,
-                    cornerIndex = index,
+                    designatedCorner = newCornerView,
+                    corner = corner,
                 )
         }
     }
@@ -284,24 +274,15 @@
         views.forEach { corner ->
             corner.setPadding(0, paddingTop, 0, 0)
 
-            val rotatedCorner = rotatedCorner(cornerForView(corner), rotation)
+            val rotatedCorner = cornerForView(corner).rotatedCorner(rotation)
             (corner.layoutParams as FrameLayout.LayoutParams).apply {
-                gravity = rotatedCorner.toGravity()
+                gravity = rotatedCorner.gravity
             }
 
             // Set the dot's view gravity to hug the status bar
             (corner.requireViewById<View>(R.id.privacy_dot).layoutParams
                     as FrameLayout.LayoutParams)
-                .gravity = rotatedCorner.innerGravity()
-        }
-    }
-
-    @UiThread
-    private fun updateCornerSizes(l: Int, r: Int, rotation: Int) {
-        views.forEach { corner ->
-            val rotatedCorner = rotatedCorner(cornerForView(corner), rotation)
-            val w = widthForCorner(rotatedCorner, l, r)
-            (corner.layoutParams as FrameLayout.LayoutParams).width = w
+                .gravity = rotatedCorner.innerGravity
         }
     }
 
@@ -419,25 +400,16 @@
         }
     }
 
-    private fun cornerForView(v: View): Int {
+    private fun cornerForView(v: View): PrivacyDotCorner {
         return when (v) {
-            tl -> TOP_LEFT
-            tr -> TOP_RIGHT
-            bl -> BOTTOM_LEFT
-            br -> BOTTOM_RIGHT
+            tl -> TopLeft
+            tr -> TopRight
+            bl -> BottomLeft
+            br -> BottomRight
             else -> throw IllegalArgumentException("not a corner view")
         }
     }
 
-    private fun rotatedCorner(corner: Int, rotation: Int): Int {
-        var modded = corner - rotation
-        if (modded < 0) {
-            modded += 4
-        }
-
-        return modded
-    }
-
     @Rotation
     private fun activeRotationForCorner(corner: View, rtl: Boolean): Int {
         // Each corner will only be visible in a single rotation, based on rtl
@@ -449,16 +421,6 @@
         }
     }
 
-    private fun widthForCorner(corner: Int, left: Int, right: Int): Int {
-        return when (corner) {
-            TOP_LEFT,
-            BOTTOM_LEFT -> left
-            TOP_RIGHT,
-            BOTTOM_RIGHT -> right
-            else -> throw IllegalArgumentException("Unknown corner")
-        }
-    }
-
     override fun initialize(topLeft: View, topRight: View, bottomLeft: View, bottomRight: View) {
         if (
             this::tl.isInitialized &&
@@ -478,9 +440,9 @@
 
         val rtl = configurationController.isLayoutRtl
         val currentRotation = RotationUtils.getExactRotation(tl.context)
-        val dc = selectDesignatedCorner(currentRotation, rtl)
+        val designatedCornerView = selectDesignatedCorner(currentRotation, rtl)
 
-        val index = dc.cornerIndex()
+        val corner = designatedCornerView.corner()
 
         mainExecutor.execute { animationScheduler.addCallback(systemStatusAnimationCallback) }
 
@@ -494,8 +456,8 @@
             nextViewState =
                 nextViewState.copy(
                     viewInitialized = true,
-                    designatedCorner = dc,
-                    cornerIndex = index,
+                    designatedCorner = designatedCornerView,
+                    corner = corner,
                     seascapeRect = left,
                     portraitRect = top,
                     landscapeRect = right,
@@ -524,7 +486,7 @@
         dlog("scheduleUpdate: ")
 
         cancelRunnable?.run()
-        cancelRunnable = uiExecutor?.executeDelayed({ processNextViewState() }, 100)
+        cancelRunnable = uiExecutor.executeDelayed({ processNextViewState() }, 100)
     }
 
     @UiThread
@@ -613,11 +575,11 @@
             }
         }
 
-    private fun View?.cornerIndex(): Int {
+    private fun View?.corner(): PrivacyDotCorner? {
         if (this != null) {
             return cornerForView(this)
         }
-        return -1
+        return null
     }
 
     // Returns [left, top, right, bottom] aka [seascape, none, landscape, upside-down]
@@ -666,35 +628,11 @@
     }
 }
 
-const val TOP_LEFT = 0
-const val TOP_RIGHT = 1
-const val BOTTOM_RIGHT = 2
-const val BOTTOM_LEFT = 3
 private const val DURATION = 160L
 private const val TAG = "PrivacyDotViewController"
 private const val DEBUG = false
 private const val DEBUG_VERBOSE = false
 
-private fun Int.toGravity(): Int {
-    return when (this) {
-        TOP_LEFT -> Gravity.TOP or Gravity.LEFT
-        TOP_RIGHT -> Gravity.TOP or Gravity.RIGHT
-        BOTTOM_LEFT -> Gravity.BOTTOM or Gravity.LEFT
-        BOTTOM_RIGHT -> Gravity.BOTTOM or Gravity.RIGHT
-        else -> throw IllegalArgumentException("Not a corner")
-    }
-}
-
-private fun Int.innerGravity(): Int {
-    return when (this) {
-        TOP_LEFT -> Gravity.CENTER_VERTICAL or Gravity.RIGHT
-        TOP_RIGHT -> Gravity.CENTER_VERTICAL or Gravity.LEFT
-        BOTTOM_LEFT -> Gravity.CENTER_VERTICAL or Gravity.RIGHT
-        BOTTOM_RIGHT -> Gravity.CENTER_VERTICAL or Gravity.LEFT
-        else -> throw IllegalArgumentException("Not a corner")
-    }
-}
-
 data class ViewState(
     val viewInitialized: Boolean = false,
     val systemPrivacyEventIsActive: Boolean = false,
@@ -707,7 +645,7 @@
     val layoutRtl: Boolean = false,
     val rotation: Int = 0,
     val paddingTop: Int = 0,
-    val cornerIndex: Int = -1,
+    val corner: PrivacyDotCorner? = null,
     val designatedCorner: View? = null,
     val contentDescription: String? = null,
 ) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt
index 682a9ff..77ec65b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt
@@ -29,6 +29,7 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.shade.ShadeDisplayAware
 import com.android.systemui.statusbar.notification.collection.NotificationEntry
 import com.android.systemui.statusbar.notification.collection.inflation.BindEventManager
 import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection
@@ -141,7 +142,7 @@
 @Inject
 constructor(
     bindEventManager: BindEventManager,
-    private val context: Context,
+    @ShadeDisplayAware private val context: Context,
     private val notifCollection: CommonNotifCollection,
     @Main private val mainHandler: Handler
 ) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/HeadsUpManagerPhone.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/HeadsUpManagerPhone.java
index fb42bad..0299ebc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/HeadsUpManagerPhone.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/HeadsUpManagerPhone.java
@@ -426,7 +426,7 @@
         }
         mAnimationStateHandler.setHeadsUpGoingAwayAnimationsAllowed(false);
         for (NotificationEntry entry : mEntriesToRemoveWhenReorderingAllowed) {
-            if (isHeadsUpEntry(entry.getKey())) {
+            if (entry != null && isHeadsUpEntry(entry.getKey())) {
                 // Maybe the heads-up was removed already
                 removeEntry(entry.getKey(), "mOnReorderingAllowedListener");
             }
@@ -488,6 +488,13 @@
         }
         updateTopHeadsUpFlow();
         updateHeadsUpFlow();
+        if (NotificationThrottleHun.isEnabled()) {
+            if (headsUpEntry.mEntry != null) {
+                if (mEntriesToRemoveWhenReorderingAllowed.contains(headsUpEntry.mEntry)) {
+                    mEntriesToRemoveWhenReorderingAllowed.remove(headsUpEntry.mEntry);
+                }
+            }
+        }
     }
 
     private void updateTopHeadsUpFlow() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/EntryWithDismissStats.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/EntryWithDismissStats.kt
new file mode 100644
index 0000000..48a8c01
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/EntryWithDismissStats.kt
@@ -0,0 +1,46 @@
+/*
+ * 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.collection
+
+import com.android.internal.statusbar.NotificationVisibility
+import com.android.systemui.statusbar.notification.collection.notifcollection.DismissedByUserStats
+
+/**
+ * A holder class for a [NotificationEntry] and an associated [DismissedByUserStats], used by
+ * [NotifCollection] for handling dismissal.
+ */
+data class EntryWithDismissStats(val entry: NotificationEntry, val stats: DismissedByUserStats) {
+    /**
+     * Creates deep a copy of this object, but with the entry, key and rank updated to correspond to
+     * the given entry.
+     */
+    fun copyForEntry(newEntry: NotificationEntry) =
+        EntryWithDismissStats(
+            entry = newEntry,
+            stats =
+                DismissedByUserStats(
+                    stats.dismissalSurface,
+                    stats.dismissalSentiment,
+                    NotificationVisibility.obtain(
+                        newEntry.key,
+                        newEntry.ranking.rank,
+                        stats.notificationVisibility.count,
+                        /* visible= */ false,
+                    ),
+                ),
+        )
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
index 7b3a93a..cf9ee61 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
@@ -39,6 +39,7 @@
 import static android.service.notification.NotificationListenerService.REASON_UNAUTOBUNDLED;
 import static android.service.notification.NotificationListenerService.REASON_USER_STOPPED;
 
+import static com.android.systemui.Flags.notificationsDismissPrunedSummaries;
 import static com.android.systemui.statusbar.notification.NotificationUtils.logKey;
 import static com.android.systemui.statusbar.notification.collection.NotificationEntry.DismissState.DISMISSED;
 import static com.android.systemui.statusbar.notification.collection.NotificationEntry.DismissState.NOT_DISMISSED;
@@ -62,7 +63,6 @@
 import android.service.notification.StatusBarNotification;
 import android.util.ArrayMap;
 import android.util.Log;
-import android.util.Pair;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
@@ -111,6 +111,7 @@
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
@@ -273,15 +274,19 @@
      * Dismisses multiple notifications on behalf of the user.
      */
     public void dismissNotifications(
-            List<Pair<NotificationEntry, DismissedByUserStats>> entriesToDismiss) {
+            List<EntryWithDismissStats> entriesToDismiss) {
         Assert.isMainThread();
         checkForReentrantCall();
 
+        if (notificationsDismissPrunedSummaries()) {
+            entriesToDismiss = includeSummariesToDismiss(entriesToDismiss);
+        }
+
         final int entryCount = entriesToDismiss.size();
         final List<NotificationEntry> entriesToLocallyDismiss = new ArrayList<>();
         for (int i = 0; i < entriesToDismiss.size(); i++) {
-            NotificationEntry entry = entriesToDismiss.get(i).first;
-            DismissedByUserStats stats = entriesToDismiss.get(i).second;
+            NotificationEntry entry = entriesToDismiss.get(i).getEntry();
+            DismissedByUserStats stats = entriesToDismiss.get(i).getStats();
 
             requireNonNull(stats);
             NotificationEntry storedEntry = mNotificationSet.get(entry.getKey());
@@ -336,13 +341,32 @@
         dispatchEventsAndRebuildList("dismissNotifications");
     }
 
+    private List<EntryWithDismissStats> includeSummariesToDismiss(
+            List<EntryWithDismissStats> entriesToDismiss) {
+        final HashSet<NotificationEntry> entriesSet = new HashSet<>(entriesToDismiss.size());
+        for (EntryWithDismissStats entryToStats : entriesToDismiss) {
+            entriesSet.add(entryToStats.getEntry());
+        }
+
+        final List<EntryWithDismissStats> entriesPlusSummaries =
+                new ArrayList<>(entriesToDismiss.size() + 1);
+        for (EntryWithDismissStats entryToStats : entriesToDismiss) {
+            entriesPlusSummaries.add(entryToStats);
+            NotificationEntry summary = fetchSummaryToDismiss(entryToStats.getEntry());
+            if (summary != null && !entriesSet.contains(summary)) {
+                entriesPlusSummaries.add(entryToStats.copyForEntry(summary));
+            }
+        }
+        return entriesPlusSummaries;
+    }
+
     /**
      * Dismisses a single notification on behalf of the user.
      */
     public void dismissNotification(
             NotificationEntry entry,
             @NonNull DismissedByUserStats stats) {
-        dismissNotifications(List.of(new Pair<>(entry, stats)));
+        dismissNotifications(List.of(new EntryWithDismissStats(entry, stats)));
     }
 
     /**
@@ -1062,6 +1086,16 @@
         }
     }
 
+    @Nullable
+    private NotificationEntry fetchSummaryToDismiss(NotificationEntry entry) {
+        if (isOnlyChildInGroup(entry)) {
+            String group = entry.getSbn().getGroupKey();
+            NotificationEntry summary = getGroupSummary(group);
+            if (summary != null && isDismissable(summary)) return summary;
+        }
+        return null;
+    }
+
     /** A single method interface that callers can pass in when registering future dismissals */
     public interface DismissedByUserStatsCreator {
         DismissedByUserStats createDismissedByUserStats(NotificationEntry entry);
@@ -1092,16 +1126,6 @@
                     + ">";
         }
 
-        @Nullable
-        private NotificationEntry fetchSummaryToDismiss(NotificationEntry entry) {
-            if (isOnlyChildInGroup(entry)) {
-                String group = entry.getSbn().getGroupKey();
-                NotificationEntry summary = getGroupSummary(group);
-                if (summary != null && isDismissable(summary)) return summary;
-            }
-            return null;
-        }
-
         /** called when the entry has been removed from the collection */
         public void onSystemServerCancel(@CancellationReason int cancellationReason) {
             Assert.isMainThread();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt
index ec8566b..c7b47ee 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt
@@ -20,11 +20,15 @@
 import android.util.ArrayMap
 import android.util.ArraySet
 import com.android.internal.annotations.VisibleForTesting
+import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.statusbar.NotificationRemoteInputManager
+import com.android.systemui.statusbar.chips.notification.domain.interactor.StatusBarNotificationChipsInteractor
+import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips
 import com.android.systemui.statusbar.notification.NotifPipelineFlags
 import com.android.systemui.statusbar.notification.collection.GroupEntry
 import com.android.systemui.statusbar.notification.collection.ListEntry
+import com.android.systemui.statusbar.notification.collection.NotifCollection
 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
@@ -48,6 +52,8 @@
 import com.android.systemui.util.time.SystemClock
 import java.util.function.Consumer
 import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
 
 /**
  * Coordinates heads up notification (HUN) interactions with the notification pipeline based on the
@@ -67,16 +73,19 @@
 class HeadsUpCoordinator
 @Inject
 constructor(
+    @Application private val applicationScope: CoroutineScope,
     private val mLogger: HeadsUpCoordinatorLogger,
     private val mSystemClock: SystemClock,
+    private val notifCollection: NotifCollection,
     private val mHeadsUpManager: HeadsUpManager,
     private val mHeadsUpViewBinder: HeadsUpViewBinder,
     private val mVisualInterruptionDecisionProvider: VisualInterruptionDecisionProvider,
     private val mRemoteInputManager: NotificationRemoteInputManager,
     private val mLaunchFullScreenIntentProvider: LaunchFullScreenIntentProvider,
     private val mFlags: NotifPipelineFlags,
+    private val statusBarNotificationChipsInteractor: StatusBarNotificationChipsInteractor,
     @IncomingHeader private val mIncomingHeaderController: NodeController,
-    @Main private val mExecutor: DelayableExecutor
+    @Main private val mExecutor: DelayableExecutor,
 ) : Coordinator {
     private val mEntriesBindingUntil = ArrayMap<String, Long>()
     private val mEntriesUpdateTimes = ArrayMap<String, Long>()
@@ -98,6 +107,52 @@
         pipeline.addPromoter(mNotifPromoter)
         pipeline.addNotificationLifetimeExtender(mLifetimeExtender)
         mRemoteInputManager.addActionPressListener(mActionPressListener)
+
+        if (StatusBarNotifChips.isEnabled) {
+            applicationScope.launch {
+                statusBarNotificationChipsInteractor.promotedNotificationChipTapEvent.collect {
+                    showPromotedNotificationHeadsUp(it)
+                }
+            }
+        }
+    }
+
+    /**
+     * Shows the promoted notification with the given [key] as heads-up.
+     *
+     * Must be run on the main thread.
+     */
+    private fun showPromotedNotificationHeadsUp(key: String) {
+        StatusBarNotifChips.assertInNewMode()
+        mLogger.logShowPromotedNotificationHeadsUp(key)
+
+        val entry = notifCollection.getEntry(key)
+        if (entry == null) {
+            mLogger.logPromotedNotificationForHeadsUpNotFound(key)
+            return
+        }
+        // TODO(b/364653005): Validate that the given key indeed matches a promoted notification,
+        // not just any notification.
+
+        val posted =
+            PostedEntry(
+                entry,
+                wasAdded = false,
+                wasUpdated = false,
+                // Force-set this notification to show heads-up.
+                // TODO(b/364653005): This means that if you tap on the second notification chip,
+                // then it moves to become the first chip because whatever notification is showing
+                // heads-up is considered to be the top notification.
+                shouldHeadsUpEver = true,
+                shouldHeadsUpAgain = true,
+                isHeadsUpEntry = mHeadsUpManager.isHeadsUpEntry(entry.key),
+                isBinding = isEntryBinding(entry),
+            )
+
+        mExecutor.execute {
+            mPostedEntries[entry.key] = posted
+            mNotifPromoter.invalidateList("showPromotedNotificationHeadsUp: ${entry.logKey}")
+        }
     }
 
     private fun onHeadsUpViewBound(entry: NotificationEntry) {
@@ -222,7 +277,7 @@
                 logicalSummary.setInterruption()
                 mLogger.logSummaryMarkedInterrupted(
                     logicalSummary.key,
-                    childToReceiveParentHeadsUp.key
+                    childToReceiveParentHeadsUp.key,
                 )
 
                 // If the summary was not attached, then remove the heads up from the detached
@@ -246,12 +301,12 @@
                     handlePostedEntry(
                         summaryUpdateForRemoval,
                         hunMutator,
-                        scenario = "detached-summary-remove-heads-up"
+                        scenario = "detached-summary-remove-heads-up",
                     )
                 } else if (summaryUpdate != null) {
                     mLogger.logPostedEntryWillNotEvaluate(
                         summaryUpdate,
-                        reason = "attached-summary-transferred"
+                        reason = "attached-summary-transferred",
                     )
                 }
 
@@ -270,14 +325,14 @@
                             handlePostedEntry(
                                 postedEntry,
                                 hunMutator,
-                                scenario = "child-heads-up-transfer-target-$targetType"
+                                scenario = "child-heads-up-transfer-target-$targetType",
                             )
                             didHeadsUpChildToReceiveParentHeadsUp = true
                         } else {
                             handlePostedEntry(
                                 postedEntry,
                                 hunMutator,
-                                scenario = "child-heads-up-non-target"
+                                scenario = "child-heads-up-non-target",
                             )
                         }
                     }
@@ -301,7 +356,7 @@
                     handlePostedEntry(
                         posted,
                         hunMutator,
-                        scenario = "non-posted-child-heads-up-transfer-target-$targetType"
+                        scenario = "non-posted-child-heads-up-transfer-target-$targetType",
                     )
                 }
             }
@@ -345,10 +400,7 @@
             .filter { !it.sbn.notification.isGroupSummary }
             .filter { locationLookupByKey(it.key) != GroupLocation.Detached }
             .sortedWith(
-                compareBy(
-                    { !mPostedEntries.contains(it.key) },
-                    { -it.sbn.notification.getWhen() },
-                )
+                compareBy({ !mPostedEntries.contains(it.key) }, { -it.sbn.notification.getWhen() })
             )
             .firstOrNull()
 
@@ -499,7 +551,7 @@
                         mHeadsUpManager.removeNotification(
                             posted.key,
                             /* removeImmediately= */ false,
-                            "onEntryUpdated"
+                            "onEntryUpdated",
                         )
                     } else if (posted.isBinding) {
                         // Don't let the bind finish
@@ -527,7 +579,7 @@
                     mHeadsUpManager.removeNotification(
                         entry.key,
                         removeImmediatelyForRemoteInput,
-                        "onEntryRemoved, reason: $reason"
+                        "onEntryRemoved, reason: $reason",
                     )
                 }
             }
@@ -593,7 +645,7 @@
                             // for FSI reconsideration
                             mLogger.logEntryDisqualifiedFromFullScreen(
                                 entry.key,
-                                decision.logReason
+                                decision.logReason,
                             )
                             mVisualInterruptionDecisionProvider.logFullScreenIntentDecision(
                                 decision
@@ -619,7 +671,7 @@
                         mLogger.logEntryUpdatedByRanking(
                             entry.key,
                             shouldHeadsUpEver,
-                            decision.logReason
+                            decision.logReason,
                         )
                         onEntryUpdated(entry)
                     }
@@ -731,10 +783,10 @@
                                     entry.key, /* releaseImmediately */
                                     true,
                                     "cancel lifetime extension - extended for reason: " +
-                                        "$reason, isSticky: true"
+                                        "$reason, isSticky: true",
                                 )
                             },
-                            removeAfterMillis
+                            removeAfterMillis,
                         )
                 } else {
                     mExecutor.execute {
@@ -742,7 +794,7 @@
                             entry.key, /* releaseImmediately */
                             false,
                             "lifetime extension - extended for reason: $reason" +
-                                ", isSticky: false"
+                                ", isSticky: false",
                         )
                     }
                     mNotifsExtendingLifetime[entry] = null
@@ -873,7 +925,7 @@
     Detached,
     Isolated,
     Summary,
-    Child
+    Child,
 }
 
 private fun Map<String, GroupLocation>.getLocation(key: String): GroupLocation =
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorLogger.kt
index 1a521d7..e443a04 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorLogger.kt
@@ -138,4 +138,22 @@
             { "marked group summary as interrupted: $str1 for alert transfer to child: $str2" },
         )
     }
+
+    fun logShowPromotedNotificationHeadsUp(key: String) {
+        buffer.log(
+            TAG,
+            LogLevel.DEBUG,
+            { str1 = key },
+            { "requesting promoted entry to show heads up: $str1" },
+        )
+    }
+
+    fun logPromotedNotificationForHeadsUpNotFound(key: String) {
+        buffer.log(
+            TAG,
+            LogLevel.DEBUG,
+            { str1 = key },
+            { "could not find promoted entry, so not showing heads up: $str1" },
+        )
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAppearanceCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAppearanceCoordinator.kt
index df694bb..de868d4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAppearanceCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAppearanceCoordinator.kt
@@ -18,6 +18,7 @@
 
 import android.content.Context
 import com.android.systemui.res.R
+import com.android.systemui.shade.ShadeDisplayAware
 import com.android.systemui.statusbar.notification.AssistantFeedbackController
 import com.android.systemui.statusbar.notification.collection.ListEntry
 import com.android.systemui.statusbar.notification.collection.NotifPipeline
@@ -33,7 +34,7 @@
  */
 @CoordinatorScope
 class RowAppearanceCoordinator @Inject internal constructor(
-    context: Context,
+    @ShadeDisplayAware context: Context,
     private var mAssistantFeedbackController: AssistantFeedbackController,
     private var mSectionStyleProvider: SectionStyleProvider
 ) : Coordinator {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinator.kt
index b8a9594..db778b80 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinator.kt
@@ -30,6 +30,7 @@
 import com.android.systemui.statusbar.policy.ConfigurationController
 import com.android.systemui.util.Compile
 import com.android.app.tracing.traceSection
+import com.android.systemui.shade.ShadeDisplayAware
 import javax.inject.Inject
 
 /**
@@ -39,7 +40,7 @@
  */
 @CoordinatorScope
 class ViewConfigCoordinator @Inject internal constructor(
-    private val mConfigurationController: ConfigurationController,
+    @ShadeDisplayAware private val mConfigurationController: ConfigurationController,
     private val mLockscreenUserManager: NotificationLockscreenUserManager,
     private val mGutsManager: NotificationGutsManager,
     private val mKeyguardUpdateMonitor: KeyguardUpdateMonitor,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java
index 9d5d7a1..e6d22b0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java
@@ -37,6 +37,7 @@
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.flags.Flags;
+import com.android.systemui.shade.ShadeDisplayAware;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
 import com.android.systemui.statusbar.NotificationPresenter;
 import com.android.systemui.statusbar.NotificationRemoteInputManager;
@@ -86,7 +87,7 @@
 
     @Inject
     public NotificationRowBinderImpl(
-            Context context,
+            @ShadeDisplayAware Context context,
             NotificationMessagingUtil notificationMessagingUtil,
             NotificationRemoteInputManager notificationRemoteInputManager,
             NotificationLockscreenUserManager notificationLockscreenUserManager,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewManager.kt
index 1935866..cab4c1c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewManager.kt
@@ -27,6 +27,7 @@
 import com.android.systemui.statusbar.notification.collection.provider.SectionHeaderVisibilityProvider
 import com.android.systemui.statusbar.notification.stack.NotificationListContainer
 import com.android.app.tracing.traceSection
+import com.android.systemui.shade.ShadeDisplayAware
 import dagger.assisted.Assisted
 import dagger.assisted.AssistedFactory
 import dagger.assisted.AssistedInject
@@ -36,7 +37,7 @@
  * currently populate the notification shade.
  */
 class ShadeViewManager @AssistedInject constructor(
-    context: Context,
+    @ShadeDisplayAware context: Context,
     @Assisted listContainer: NotificationListContainer,
     @Assisted private val stackController: NotifStackController,
     mediaContainerController: MediaContainerController,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/ReferenceNotificationsModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/ReferenceNotificationsModule.kt
new file mode 100644
index 0000000..925d4a5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/ReferenceNotificationsModule.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.dagger
+
+import com.android.systemui.statusbar.notification.row.NotificationRowModule
+import dagger.Module
+
+/**
+ * A module that includes the standard notifications classes that most SysUI variants need. Variants
+ * are free to not include this module and instead write a custom notifications module.
+ */
+@Module(includes = [NotificationsModule::class, NotificationRowModule::class])
+object ReferenceNotificationsModule
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/shared/NotifRedesignFooter.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/shared/NotifRedesignFooter.kt
new file mode 100644
index 0000000..0307d90
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/shared/NotifRedesignFooter.kt
@@ -0,0 +1,61 @@
+/*
+ * 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.footer.shared
+
+import com.android.systemui.Flags
+import com.android.systemui.flags.FlagToken
+import com.android.systemui.flags.RefactorFlagUtils
+
+/** Helper for reading or using the flag state for the footer redesign. */
+@Suppress("NOTHING_TO_INLINE")
+object NotifRedesignFooter {
+    /** The aconfig flag name */
+    const val FLAG_NAME = Flags.FLAG_NOTIFICATIONS_REDESIGN_FOOTER_VIEW
+
+    /** A token used for dependency declaration */
+    val token: FlagToken
+        get() = FlagToken(FLAG_NAME, isEnabled)
+
+    /** Is the refactor enabled */
+    @JvmStatic
+    inline val isEnabled
+        get() = Flags.notificationsRedesignFooterView()
+
+    /**
+     * Called to ensure code is only run when the flag is enabled. This protects users from the
+     * unintended behaviors caused by accidentally running new logic, while also crashing on an eng
+     * build to ensure that the refactor author catches issues in testing.
+     */
+    @JvmStatic
+    inline fun isUnexpectedlyInLegacyMode() =
+        RefactorFlagUtils.isUnexpectedlyInLegacyMode(isEnabled, FLAG_NAME)
+
+    /**
+     * Called to ensure code is only run when the flag is enabled. This will throw an exception if
+     * the flag is not enabled to ensure that the refactor author catches issues in testing.
+     * Caution!! Using this check incorrectly will cause crashes in nextfood builds!
+     */
+    @JvmStatic
+    inline fun assertInNewMode() = RefactorFlagUtils.assertInNewMode(isEnabled, FLAG_NAME)
+
+    /**
+     * Called to ensure code is only run when the flag is disabled. This will throw an exception if
+     * the flag is enabled to ensure that the refactor author catches issues in testing.
+     */
+    @JvmStatic
+    inline fun assertInLegacyMode() = RefactorFlagUtils.assertInLegacyMode(isEnabled, FLAG_NAME)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterView.java
index 5a616df..96f47e5 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
@@ -43,6 +43,7 @@
 import com.android.systemui.res.R;
 import com.android.systemui.statusbar.notification.ColorUpdateLogger;
 import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor;
+import com.android.systemui.statusbar.notification.footer.shared.NotifRedesignFooter;
 import com.android.systemui.statusbar.notification.row.FooterViewButton;
 import com.android.systemui.statusbar.notification.row.StackScrollerDecorView;
 import com.android.systemui.statusbar.notification.stack.AnimationProperties;
@@ -59,6 +60,9 @@
 
     private FooterViewButton mClearAllButton;
     private FooterViewButton mManageOrHistoryButton;
+    // The settings & history buttons replace the single manage/history button in the redesign
+    private FooterViewButton mSettingsButton;
+    private FooterViewButton mHistoryButton;
     private boolean mShouldBeHidden;
     private boolean mShowHistory;
     // String cache, for performance reasons.
@@ -103,11 +107,31 @@
         setClearAllButtonVisible(visible, animate, /* onAnimationEnded = */ null);
     }
 
-    /** Set the visibility of the "Manage"/"History" button to {@code visible}. */
+    /**
+     * Set the visibility of the "Manage"/"History" button to {@code visible}. This is replaced by
+     * two separate buttons in the redesign.
+     */
     public void setManageOrHistoryButtonVisible(boolean visible) {
+        NotifRedesignFooter.assertInLegacyMode();
         mManageOrHistoryButton.setVisibility(visible ? View.VISIBLE : View.GONE);
     }
 
+    /** Set the visibility of the Settings button to {@code visible}. */
+    public void setSettingsButtonVisible(boolean visible) {
+        if (NotifRedesignFooter.isUnexpectedlyInLegacyMode()) {
+            return;
+        }
+        mSettingsButton.setVisibility(visible ? View.VISIBLE : View.GONE);
+    }
+
+    /** Set the visibility of the History button to {@code visible}. */
+    public void setHistoryButtonVisible(boolean visible) {
+        if (NotifRedesignFooter.isUnexpectedlyInLegacyMode()) {
+            return;
+        }
+        mHistoryButton.setVisibility(visible ? View.VISIBLE : View.GONE);
+    }
+
     /**
      * Set the visibility of the "Clear all" button to {@code visible}. Animate the change if
      * {@code animate} is true.
@@ -183,6 +207,7 @@
 
     /** Set the text label for the "Manage"/"History" button. */
     public void setManageOrHistoryButtonText(@StringRes int textId) {
+        NotifRedesignFooter.assertInLegacyMode();
         if (FooterViewRefactor.isUnexpectedlyInLegacyMode()) return;
         if (mManageOrHistoryButtonTextId == textId) {
             return; // nothing changed
@@ -192,6 +217,7 @@
     }
 
     private void updateManageOrHistoryButtonText() {
+        NotifRedesignFooter.assertInLegacyMode();
         if (mManageOrHistoryButtonTextId == 0) {
             return; // not initialized yet
         }
@@ -200,6 +226,7 @@
 
     /** Set the accessibility content description for the "Clear all" button. */
     public void setManageOrHistoryButtonDescription(@StringRes int contentDescriptionId) {
+        NotifRedesignFooter.assertInLegacyMode();
         if (FooterViewRefactor.isUnexpectedlyInLegacyMode()) {
             return;
         }
@@ -211,6 +238,7 @@
     }
 
     private void updateManageOrHistoryButtonDescription() {
+        NotifRedesignFooter.assertInLegacyMode();
         if (mManageOrHistoryButtonDescriptionId == 0) {
             return; // not initialized yet
         }
@@ -269,7 +297,12 @@
         }
         super.onFinishInflate();
         mClearAllButton = (FooterViewButton) findSecondaryView();
-        mManageOrHistoryButton = findViewById(R.id.manage_text);
+        if (NotifRedesignFooter.isEnabled()) {
+            mSettingsButton = findViewById(R.id.settings_button);
+            mHistoryButton = findViewById(R.id.history_button);
+        } else {
+            mManageOrHistoryButton = findViewById(R.id.manage_text);
+        }
         mSeenNotifsFooterTextView = findViewById(R.id.unlock_prompt_footer);
         if (!FooterViewRefactor.isEnabled()) {
             updateResources();
@@ -300,8 +333,28 @@
         }
     }
 
-    /** Set onClickListener for the manage/history button. */
+    /** Set onClickListener for the notification settings button. */
+    public void setSettingsButtonClickListener(OnClickListener listener) {
+        if (NotifRedesignFooter.isUnexpectedlyInLegacyMode()) {
+            return;
+        }
+        mSettingsButton.setOnClickListener(listener);
+    }
+
+    /** Set onClickListener for the notification history button. */
+    public void setHistoryButtonClickListener(OnClickListener listener) {
+        if (NotifRedesignFooter.isUnexpectedlyInLegacyMode()) {
+            return;
+        }
+        mHistoryButton.setOnClickListener(listener);
+    }
+
+    /**
+     * Set onClickListener for the manage/history button. This is replaced by two separate buttons
+     * in the redesign.
+     */
     public void setManageButtonClickListener(OnClickListener listener) {
+        NotifRedesignFooter.assertInLegacyMode();
         mManageOrHistoryButton.setOnClickListener(listener);
     }
 
@@ -342,8 +395,10 @@
             updateClearAllButtonText();
             updateClearAllButtonDescription();
 
-            updateManageOrHistoryButtonText();
-            updateManageOrHistoryButtonDescription();
+            if (!NotifRedesignFooter.isEnabled()) {
+                updateManageOrHistoryButtonText();
+                updateManageOrHistoryButtonDescription();
+            }
 
             updateMessageString();
             updateMessageIcon();
@@ -420,8 +475,16 @@
         }
         mClearAllButton.setBackground(clearAllBg);
         mClearAllButton.setTextColor(onSurface);
-        mManageOrHistoryButton.setBackground(manageBg);
-        mManageOrHistoryButton.setTextColor(onSurface);
+        if (NotifRedesignFooter.isEnabled()) {
+            mSettingsButton.setBackground(manageBg);
+            mSettingsButton.setCompoundDrawableTintList(ColorStateList.valueOf(onSurface));
+
+            mHistoryButton.setBackground(manageBg);
+            mHistoryButton.setCompoundDrawableTintList(ColorStateList.valueOf(onSurface));
+        } else {
+            mManageOrHistoryButton.setBackground(manageBg);
+            mManageOrHistoryButton.setTextColor(onSurface);
+        }
         mSeenNotifsFooterTextView.setTextColor(onSurface);
         mSeenNotifsFooterTextView.setCompoundDrawableTintList(ColorStateList.valueOf(onSurface));
         ColorUpdateLogger colorUpdateLogger = ColorUpdateLogger.getInstance();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewbinder/FooterViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewbinder/FooterViewBinder.kt
index 2ec7f53..3383ce9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewbinder/FooterViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewbinder/FooterViewBinder.kt
@@ -18,9 +18,13 @@
 
 import android.view.View
 import androidx.lifecycle.lifecycleScope
+import com.android.app.tracing.coroutines.launchTraced as launch
+import com.android.internal.jank.InteractionJankMonitor
 import com.android.systemui.lifecycle.repeatWhenAttached
 import com.android.systemui.statusbar.notification.NotificationActivityStarter
+import com.android.systemui.statusbar.notification.NotificationActivityStarter.SettingsIntent
 import com.android.systemui.statusbar.notification.emptyshade.shared.ModesEmptyShadeFix
+import com.android.systemui.statusbar.notification.footer.shared.NotifRedesignFooter
 import com.android.systemui.statusbar.notification.footer.ui.view.FooterView
 import com.android.systemui.statusbar.notification.footer.ui.viewmodel.FooterViewModel
 import com.android.systemui.util.ui.isAnimating
@@ -28,7 +32,6 @@
 import com.android.systemui.util.ui.value
 import kotlinx.coroutines.DisposableHandle
 import kotlinx.coroutines.coroutineScope
-import com.android.app.tracing.coroutines.launchTraced as launch
 
 /** Binds a [FooterView] to its [view model][FooterViewModel]. */
 object FooterViewBinder {
@@ -63,14 +66,19 @@
         notificationActivityStarter: NotificationActivityStarter,
     ) = coroutineScope {
         launch { bindClearAllButton(footer, viewModel, clearAllNotifications) }
-        launch {
-            bindManageOrHistoryButton(
-                footer,
-                viewModel,
-                launchNotificationSettings,
-                launchNotificationHistory,
-                notificationActivityStarter,
-            )
+        if (!NotifRedesignFooter.isEnabled) {
+            launch {
+                bindManageOrHistoryButton(
+                    footer,
+                    viewModel,
+                    launchNotificationSettings,
+                    launchNotificationHistory,
+                    notificationActivityStarter,
+                )
+            }
+        } else {
+            launch { bindSettingsButton(footer, viewModel, notificationActivityStarter) }
+            launch { bindHistoryButton(footer, viewModel, notificationActivityStarter) }
         }
         launch { bindMessage(footer, viewModel) }
     }
@@ -114,6 +122,50 @@
         }
     }
 
+    private suspend fun bindSettingsButton(
+        footer: FooterView,
+        viewModel: FooterViewModel,
+        notificationActivityStarter: NotificationActivityStarter,
+    ) = coroutineScope {
+        val settingsIntent =
+            SettingsIntent.forNotificationSettings(
+                cujType = InteractionJankMonitor.CUJ_SHADE_APP_LAUNCH_FROM_HISTORY_BUTTON
+            )
+        val onClickListener = { view: View ->
+            notificationActivityStarter.startSettingsIntent(view, settingsIntent)
+        }
+        footer.setSettingsButtonClickListener(onClickListener)
+
+        launch {
+            // NOTE: This visibility change is never animated. We also don't need to do anything
+            // special about the onClickListener here, since we're changing the visibility to
+            // GONE so it won't be clickable anyway.
+            viewModel.settingsButtonVisible.collect { footer.setSettingsButtonVisible(it) }
+        }
+    }
+
+    private suspend fun bindHistoryButton(
+        footer: FooterView,
+        viewModel: FooterViewModel,
+        notificationActivityStarter: NotificationActivityStarter,
+    ) = coroutineScope {
+        val settingsIntent =
+            SettingsIntent.forNotificationHistory(
+                cujType = InteractionJankMonitor.CUJ_SHADE_APP_LAUNCH_FROM_HISTORY_BUTTON
+            )
+        val onClickListener = { view: View ->
+            notificationActivityStarter.startSettingsIntent(view, settingsIntent)
+        }
+        footer.setHistoryButtonClickListener(onClickListener)
+
+        launch {
+            // NOTE: This visibility change is never animated. We also don't need to do anything
+            // special about the onClickListener here, since we're changing the visibility to
+            // GONE so it won't be clickable anyway.
+            viewModel.historyButtonVisible.collect { footer.setHistoryButtonVisible(it) }
+        }
+    }
+
     private suspend fun bindManageOrHistoryButton(
         footer: FooterView,
         viewModel: FooterViewModel,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModel.kt
index a3f4cd2..e724935 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModel.kt
@@ -95,10 +95,13 @@
                     .toAnimatedValueFlow(),
         )
 
+    // Settings buttons are not visible when the message is.
+    val settingsButtonVisible: Flow<Boolean> = message.isVisible.map { !it }
+    val historyButtonVisible: Flow<Boolean> = message.isVisible.map { !it }
+
     val manageButtonShouldLaunchHistory =
         notificationSettingsInteractor.isNotificationHistoryEnabled
 
-    // TODO(b/366003631): When inlining the flag, consider adding this to FooterButtonViewModel.
     val manageOrHistoryButtonClick: Flow<SettingsIntent> by lazy {
         if (ModesEmptyShadeFix.isUnexpectedlyInLegacyMode()) {
             flowOf(SettingsIntent(Intent(Settings.ACTION_NOTIFICATION_SETTINGS)))
@@ -124,7 +127,11 @@
             else R.string.manage_notifications_text
         }
 
-    /** The button for managing notification settings or opening notification history. */
+    /**
+     * The button for managing notification settings or opening notification history. This is
+     * replaced by two separate buttons in the redesign. These are currently static, and therefore
+     * not modeled here, but if that changes we can also add them as FooterButtonViewModels.
+     */
     val manageOrHistoryButton: FooterButtonViewModel =
         FooterButtonViewModel(
             labelId = manageOrHistoryButtonText,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImpl.kt
index d4466f8..71cddc9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImpl.kt
@@ -29,6 +29,7 @@
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.plugins.statusbar.StatusBarStateController
 import com.android.systemui.settings.UserTracker
+import com.android.systemui.shade.ShadeDisplayAware
 import com.android.systemui.shared.notifications.domain.interactor.NotificationSettingsInteractor
 import com.android.systemui.statusbar.notification.collection.NotificationEntry
 import com.android.systemui.statusbar.notification.interruption.VisualInterruptionDecisionProvider.Decision
@@ -72,7 +73,7 @@
     private val systemSettings: SystemSettings,
     private val packageManager: PackageManager,
     private val bubbles: Optional<Bubbles>,
-    private val context: Context,
+    @ShadeDisplayAware private val context: Context,
     private val notificationManager: NotificationManager,
     private val settingsInteractor: NotificationSettingsInteractor
 ) : VisualInterruptionDecisionProvider {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/BigPictureIconManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/BigPictureIconManager.kt
index 068f23d..e233def 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/BigPictureIconManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/BigPictureIconManager.kt
@@ -32,6 +32,7 @@
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.graphics.ImageLoader
+import com.android.systemui.shade.ShadeDisplayAware
 import com.android.systemui.statusbar.notification.row.BigPictureIconManager.DrawableState.Empty
 import com.android.systemui.statusbar.notification.row.BigPictureIconManager.DrawableState.FullImage
 import com.android.systemui.statusbar.notification.row.BigPictureIconManager.DrawableState.Initial
@@ -61,7 +62,7 @@
 class BigPictureIconManager
 @Inject
 constructor(
-    private val context: Context,
+    @ShadeDisplayAware private val context: Context,
     private val imageLoader: ImageLoader,
     private val statsManager: BigPictureStatsManager,
     @Application private val scope: CoroutineScope,
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 a4c43a1..aa2a08f 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
@@ -116,7 +116,6 @@
 import com.android.systemui.statusbar.notification.stack.NotificationChildrenContainerLogger;
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
 import com.android.systemui.statusbar.notification.stack.SwipeableView;
-import com.android.systemui.statusbar.phone.ExpandHeadsUpOnInlineReply;
 import com.android.systemui.statusbar.phone.KeyguardBypassController;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
 import com.android.systemui.statusbar.policy.InflatedSmartReplyState;
@@ -2948,18 +2947,10 @@
     }
 
     public boolean isExpanded(boolean allowOnKeyguard) {
-        final boolean isHeadsUpState = ExpandHeadsUpOnInlineReply.isEnabled()
-                && canShowHeadsUp() && isHeadsUpState();
-        // System expanded should be ignored in pinned heads up state
-        final boolean isPinned = isHeadsUpState && isPinned();
-        // Heads Up Notification can be expanded when it is pinned.
-        final boolean isPinnedAndExpanded =
-                isHeadsUpState && isPinnedAndExpanded();
-
         return (!shouldShowPublic()) && (!mOnKeyguard || allowOnKeyguard)
-                && (!hasUserChangedExpansion() && !isPinned
+                && (!hasUserChangedExpansion()
                 && (isSystemExpanded() || isSystemChildExpanded())
-                || isUserExpanded() || isPinnedAndExpanded);
+                || isUserExpanded());
     }
 
     private boolean isSystemChildExpanded() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/AppIconProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/AppIconProvider.kt
index 172b76c..98d704c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/AppIconProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/AppIconProvider.kt
@@ -33,6 +33,7 @@
 import com.android.systemui.Dumpable
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dump.DumpManager
+import com.android.systemui.shade.ShadeDisplayAware
 import com.android.systemui.statusbar.notification.collection.NotifCollectionCache
 import com.android.systemui.util.asIndenting
 import com.android.systemui.util.withIncreasedIndent
@@ -67,7 +68,7 @@
 @SysUISingleton
 class AppIconProviderImpl
 @Inject
-constructor(private val sysuiContext: Context, dumpManager: DumpManager) :
+constructor(@ShadeDisplayAware private val sysuiContext: Context, dumpManager: DumpManager) :
     AppIconProvider, Dumpable {
     init {
         dumpManager.registerNormalDumpable(TAG, this)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
index 129d4ce..7771421 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
@@ -596,10 +596,12 @@
     }
 
     public void setContentHeight(int contentHeight) {
+        SceneContainerFlag.assertInLegacyMode();
         mContentHeight = contentHeight;
     }
 
     public float getContentHeight() {
+        SceneContainerFlag.assertInLegacyMode();
         return mContentHeight;
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt
index 7441c70..c9a0010 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt
@@ -20,6 +20,7 @@
 import android.view.View
 import com.android.internal.annotations.VisibleForTesting
 import com.android.systemui.media.controls.ui.controller.KeyguardMediaController
+import com.android.systemui.shade.ShadeDisplayAware
 import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager
 import com.android.systemui.statusbar.notification.SourceType
 import com.android.systemui.statusbar.notification.collection.NotificationClassificationFlag
@@ -41,7 +42,7 @@
 class NotificationSectionsManager
 @Inject
 internal constructor(
-    private val configurationController: ConfigurationController,
+    @ShadeDisplayAware private val configurationController: ConfigurationController,
     private val keyguardMediaController: KeyguardMediaController,
     private val sectionsFeatureManager: NotificationSectionsFeatureManager,
     private val mediaContainerController: MediaContainerController,
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 87b16ef..c1d72e4 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
@@ -615,13 +615,13 @@
             if (SceneContainerFlag.isEnabled()) {
                 return mScrollViewFields.getScrollState().isScrolledToTop();
             } else {
-                return mOwnScrollY == 0;
+                return getOwnScrollY() == 0;
             }
         }
 
         @Override
         public boolean isScrolledToBottom() {
-            return mOwnScrollY >= getScrollRange();
+            return getOwnScrollY() >= getScrollRange();
         }
 
         @Override
@@ -900,11 +900,11 @@
         drawDebugInfo(canvas, y, Color.LTGRAY,
                 /* label= */ "mAmbientState.getStackY() + mAmbientState.getStackHeight() = " + y);
 
-        y = (int) (mAmbientState.getStackY() + mIntrinsicContentHeight);
+        y = (int) (mAmbientState.getStackY() + getIntrinsicContentHeight());
         drawDebugInfo(canvas, y, Color.YELLOW,
                 /* label= */ "mAmbientState.getStackY() + mIntrinsicContentHeight = " + y);
 
-        y = mContentHeight;
+        y = getContentHeight();
         drawDebugInfo(canvas, y, Color.MAGENTA,
                 /* label= */ "mContentHeight = " + y);
 
@@ -1200,7 +1200,6 @@
         if (!SceneContainerFlag.isEnabled()) {
             setMaxLayoutHeight(getHeight());
             updateContentHeight();
-            mWallpaperInteractor.setNotificationStackAbsoluteBottom(mContentHeight);
         }
         clampScrollPosition();
         requestChildrenUpdate();
@@ -1278,7 +1277,6 @@
         if (mAmbientState.getStackTop() != stackTop) {
             mAmbientState.setStackTop(stackTop);
             onTopPaddingChanged(/* animate = */ isAddOrRemoveAnimationPending());
-            mWallpaperInteractor.setNotificationStackAbsoluteBottom((int) stackTop);
         }
     }
 
@@ -1376,7 +1374,7 @@
 
     /**
      * Updates the children views according to the stack scroll algorithm. Call this whenever
-     * modifications to {@link #mOwnScrollY} are performed to reflect it in the view layout.
+     * modifications to {@link #getOwnScrollY()} are performed to reflect it in the view layout.
      */
     private void updateChildren() {
         Trace.beginSection("NSSL#updateChildren");
@@ -1406,11 +1404,11 @@
             if (mChildrenToAddAnimated.contains(child)) {
                 final int startingPosition = getPositionInLinearLayout(child);
                 final int childHeight = getIntrinsicHeight(child) + mPaddingBetweenElements;
-                if (startingPosition < mOwnScrollY) {
+                if (startingPosition < getOwnScrollY()) {
                     // This child starts off screen, so let's keep it offscreen to keep the
                     // others visible
 
-                    setOwnScrollY(mOwnScrollY + childHeight);
+                    setOwnScrollY(getOwnScrollY() + childHeight);
                 }
             }
         }
@@ -1430,7 +1428,7 @@
             targetScroll = Math.max(0, Math.min(targetScroll, getScrollRange()));
             // Only apply the scroll if we're scrolling the view upwards, or the view is so
             // far up that it is not visible anymore.
-            if (mOwnScrollY < targetScroll || outOfViewScroll < mOwnScrollY) {
+            if (getOwnScrollY() < targetScroll || outOfViewScroll < getOwnScrollY()) {
                 setOwnScrollY(targetScroll);
             }
         }
@@ -1454,7 +1452,7 @@
             return;
         }
         int scrollRange = getScrollRange();
-        if (scrollRange < mOwnScrollY && !mAmbientState.isClearAllInProgress()) {
+        if (scrollRange < getOwnScrollY() && !mAmbientState.isClearAllInProgress()) {
             // if the scroll boundary updates the position of the stack,
             boolean animateStackY = scrollRange < getScrollAmountToScrollBoundary()
                     && mAnimateStackYForContentHeightChange;
@@ -1588,7 +1586,7 @@
         if (mMaxDisplayedNotifications != -1) {
             // The stack intrinsic height already contains the correct value when there is a limit
             // in the max number of notifications (e.g. as in keyguard).
-            stackEndHeight = mIntrinsicContentHeight;
+            stackEndHeight = getIntrinsicContentHeight();
         } else {
             stackEndHeight = Math.max(0f, height - bottomMargin - topPadding);
         }
@@ -1694,7 +1692,8 @@
             if (mShouldShowShelfOnly) {
                 stackHeight = getTopPadding() + mShelf.getIntrinsicHeight();
             } else if (mQsFullScreen) {
-                int stackStartPosition = mContentHeight - getTopPadding() + getIntrinsicPadding();
+                int stackStartPosition =
+                        getContentHeight() - getTopPadding() + getIntrinsicPadding();
                 int stackEndPosition = mMaxTopPadding + mShelf.getIntrinsicHeight();
                 if (stackStartPosition <= stackEndPosition) {
                     stackHeight = stackEndPosition;
@@ -1763,14 +1762,6 @@
         updateClipping();
     }
 
-    /**
-     * Return the height of the content ignoring the footer.
-     */
-    public int getIntrinsicContentHeight() {
-        SceneContainerFlag.assertInLegacyMode();
-        return (int) mIntrinsicContentHeight;
-    }
-
     public void updateClipping() {
         boolean clipped = mRequestedClipBounds != null && !mInHeadsUpPinnedMode
                 && !mHeadsUpAnimatingAway;
@@ -2074,8 +2065,8 @@
 
         // Only apply the scroll if we're scrolling the view upwards, or the view is so far up
         // that it is not visible anymore.
-        if (mOwnScrollY < targetScroll || outOfViewScroll < mOwnScrollY) {
-            mScroller.startScroll(mScrollX, mOwnScrollY, 0, targetScroll - mOwnScrollY);
+        if (getOwnScrollY() < targetScroll || outOfViewScroll < getOwnScrollY()) {
+            mScroller.startScroll(mScrollX, getOwnScrollY(), 0, targetScroll - getOwnScrollY());
             mDontReportNextOverScroll = true;
             animateScroll();
             return true;
@@ -2109,7 +2100,7 @@
         }
 
         int range = getScrollRange();
-        if (mOwnScrollY > range) {
+        if (getOwnScrollY() > range) {
             setOwnScrollY(range);
         }
     }
@@ -2202,7 +2193,7 @@
         // Top overScroll might not grab all scrolling motion,
         // we have to scroll as well.
         float scrollAmount = newTopAmount < 0 ? -newTopAmount : 0.0f;
-        float newScrollY = mOwnScrollY + scrollAmount;
+        float newScrollY = getOwnScrollY() + scrollAmount;
         if (newScrollY > range) {
             if (!mExpandedInThisMotion) {
                 float currentBottomPixels = getCurrentOverScrolledPixels(false);
@@ -2235,7 +2226,7 @@
         // Bottom overScroll might not grab all scrolling motion,
         // we have to scroll as well.
         float scrollAmount = newBottomAmount < 0 ? newBottomAmount : 0.0f;
-        float newScrollY = mOwnScrollY + scrollAmount;
+        float newScrollY = getOwnScrollY() + scrollAmount;
         if (newScrollY < 0) {
             float currentTopPixels = getCurrentOverScrolledPixels(true);
             // We overScroll on the top
@@ -2275,7 +2266,7 @@
 
     private void animateScroll() {
         if (mScroller.computeScrollOffset()) {
-            int oldY = mOwnScrollY;
+            int oldY = getOwnScrollY();
             int y = mScroller.getCurrY();
 
             if (oldY != y) {
@@ -2462,8 +2453,8 @@
                 springBack();
             } else {
                 float overScrollTop = getCurrentOverScrollAmount(true);
-                if (mOwnScrollY < 0) {
-                    notifyOverscrollTopListener(-mOwnScrollY, isRubberbanded(true));
+                if (getOwnScrollY() < 0) {
+                    notifyOverscrollTopListener(-getOwnScrollY(), isRubberbanded(true));
                 } else {
                     notifyOverscrollTopListener(overScrollTop, isRubberbanded(true));
                 }
@@ -2479,19 +2470,19 @@
      */
     private void springBack() {
         int scrollRange = getScrollRange();
-        boolean overScrolledTop = mOwnScrollY <= 0;
-        boolean overScrolledBottom = mOwnScrollY >= scrollRange;
+        boolean overScrolledTop = getOwnScrollY() <= 0;
+        boolean overScrolledBottom = getOwnScrollY() >= scrollRange;
         if (overScrolledTop || overScrolledBottom) {
             boolean onTop;
             float newAmount;
             if (overScrolledTop) {
                 onTop = true;
-                newAmount = -mOwnScrollY;
+                newAmount = -getOwnScrollY();
                 setOwnScrollY(0);
                 mDontReportNextOverScroll = true;
             } else {
                 onTop = false;
-                newAmount = mOwnScrollY - scrollRange;
+                newAmount = getOwnScrollY() - scrollRange;
                 setOwnScrollY(scrollRange);
             }
             setOverScrollAmount(newAmount, onTop, false);
@@ -2508,7 +2499,7 @@
         }
         // In current design, it only use the top HUN to treat all of HUNs
         // although there are more than one HUNs
-        int contentHeight = mContentHeight;
+        int contentHeight = getContentHeight();
         if (!isExpanded() && mInHeadsUpPinnedMode) {
             contentHeight = mHeadsUpInset + getTopHeadsUpPinnedHeight();
         }
@@ -2644,16 +2635,16 @@
                 (int) scrimTopPadding + (int) mNotificationStackSizeCalculator.computeHeight(
                         /* notificationStackScrollLayout= */ this, mMaxDisplayedNotifications,
                         shelfIntrinsicHeight);
-        mIntrinsicContentHeight = height;
+        setIntrinsicContentHeight(height);
 
         // The topPadding can be bigger than the regular padding when qs is expanded, in that
         // state the maxPanelHeight and the contentHeight should be bigger
-        mContentHeight =
-                (int) (height + Math.max(getIntrinsicPadding(), getTopPadding()) + mBottomPadding);
+        setContentHeight(
+                (int) (height + Math.max(getIntrinsicPadding(), getTopPadding()) + mBottomPadding));
         updateScrollability();
         clampScrollPosition();
         updateStackPosition();
-        mAmbientState.setContentHeight(mContentHeight);
+        mAmbientState.setContentHeight(getContentHeight());
     }
 
     @Override
@@ -2795,7 +2786,7 @@
             float topAmount = getCurrentOverScrollAmount(true);
             float bottomAmount = getCurrentOverScrollAmount(false);
             if (velocityY < 0 && topAmount > 0) {
-                setOwnScrollY(mOwnScrollY - (int) topAmount);
+                setOwnScrollY(getOwnScrollY() - (int) topAmount);
                 if (!mShouldUseSplitNotificationShade) {
                     mDontReportNextOverScroll = true;
                     setOverScrollAmount(0, true, false);
@@ -2803,7 +2794,7 @@
                 mMaxOverScroll = Math.abs(velocityY) / 1000f * getRubberBandFactor(true /* onTop */)
                         * mOverflingDistance + topAmount;
             } else if (velocityY > 0 && bottomAmount > 0) {
-                setOwnScrollY((int) (mOwnScrollY + bottomAmount));
+                setOwnScrollY((int) (getOwnScrollY() + bottomAmount));
                 setOverScrollAmount(0, false, false);
                 mMaxOverScroll = Math.abs(velocityY) / 1000f
                         * getRubberBandFactor(false /* onTop */) * mOverflingDistance
@@ -2817,8 +2808,8 @@
             if (mExpandedInThisMotion) {
                 minScrollY = Math.min(minScrollY, mMaxScrollAfterExpand);
             }
-            mScroller.fling(mScrollX, mOwnScrollY, 1, velocityY, 0, 0, 0, minScrollY, 0,
-                    mExpandedInThisMotion && mOwnScrollY >= 0 ? 0 : Integer.MAX_VALUE / 2);
+            mScroller.fling(mScrollX, getOwnScrollY(), 1, velocityY, 0, 0, 0, minScrollY, 0,
+                    mExpandedInThisMotion && getOwnScrollY() >= 0 ? 0 : Integer.MAX_VALUE / 2);
 
             animateScroll();
         }
@@ -3139,11 +3130,11 @@
         final int scrollBoundaryStart = getScrollAmountToScrollBoundary();
         mAnimateStackYForContentHeightChange = true;
         // This is reset onLayout
-        if (endPosition <= mOwnScrollY - scrollBoundaryStart) {
+        if (endPosition <= getOwnScrollY() - scrollBoundaryStart) {
             // This child is fully scrolled of the top, so we have to deduct its height from the
             // scrollPosition
-            setOwnScrollY(mOwnScrollY - childHeight);
-        } else if (startingPosition < mOwnScrollY - scrollBoundaryStart) {
+            setOwnScrollY(getOwnScrollY() - childHeight);
+        } else if (startingPosition < getOwnScrollY() - scrollBoundaryStart) {
             // This child is currently being scrolled into, set the scroll position to the
             // start of this child
             setOwnScrollY(startingPosition + scrollBoundaryStart);
@@ -3765,7 +3756,7 @@
                         if (vscroll != 0) {
                             final int delta = (int) (vscroll * getVerticalScrollFactor());
                             final int range = getScrollRange();
-                            int oldScrollY = mOwnScrollY;
+                            int oldScrollY = getOwnScrollY();
                             int newScrollY = oldScrollY - delta;
                             if (newScrollY < 0) {
                                 newScrollY = 0;
@@ -3882,7 +3873,7 @@
                     if (scrollAmount != 0.0f) {
                         // The scrolling motion could not be compensated with the
                         // existing overScroll, we have to scroll the view
-                        customOverScrollBy((int) scrollAmount, mOwnScrollY,
+                        customOverScrollBy((int) scrollAmount, getOwnScrollY(),
                                 range, getHeight() / 2);
                         // If we're scrolling, leavebehinds should be dismissed
                         mController.checkSnoozeLeavebehind();
@@ -3914,7 +3905,7 @@
                                     onOverScrollFling(false, initialVelocity);
                                 }
                             } else {
-                                if (mScroller.springBack(mScrollX, mOwnScrollY, 0, 0, 0,
+                                if (mScroller.springBack(mScrollX, getOwnScrollY(), 0, 0, 0,
                                         getScrollRange())) {
                                     animateScroll();
                                 }
@@ -3928,7 +3919,7 @@
                 break;
             case ACTION_CANCEL:
                 if (mIsBeingDragged && getChildCount() > 0) {
-                    if (mScroller.springBack(mScrollX, mOwnScrollY, 0, 0, 0,
+                    if (mScroller.springBack(mScrollX, getOwnScrollY(), 0, 0, 0,
                             getScrollRange())) {
                         animateScroll();
                     }
@@ -4214,7 +4205,7 @@
                 setIsBeingDragged(false);
                 mActivePointerId = INVALID_POINTER;
                 recycleVelocityTracker();
-                if (mScroller.springBack(mScrollX, mOwnScrollY, 0, 0, 0, getScrollRange())) {
+                if (mScroller.springBack(mScrollX, getOwnScrollY(), 0, 0, 0, getScrollRange())) {
                     animateScroll();
                 }
                 break;
@@ -4312,10 +4303,10 @@
                         getHeight() - mPaddingBottom - getTopPadding() - mPaddingTop
                                 - mShelf.getIntrinsicHeight();
                 final int targetScrollY = Math.max(0,
-                        Math.min(mOwnScrollY + direction * viewportHeight, getScrollRange()));
-                if (targetScrollY != mOwnScrollY) {
-                    mScroller.startScroll(mScrollX, mOwnScrollY, 0,
-                            targetScrollY - mOwnScrollY);
+                        Math.min(getOwnScrollY() + direction * viewportHeight, getScrollRange()));
+                if (targetScrollY != getOwnScrollY()) {
+                    mScroller.startScroll(mScrollX, getOwnScrollY(), 0,
+                            targetScrollY - getOwnScrollY());
                     animateScroll();
                     return true;
                 }
@@ -4357,9 +4348,9 @@
             // it is based on notifications bottom, which is lower on split shade.
             // Here we prefer to use at least a minimum height defined for split shade.
             // Otherwise the expansion motion is too fast.
-            contentHeight = Math.max(mSplitShadeMinContentHeight, mContentHeight);
+            contentHeight = Math.max(mSplitShadeMinContentHeight, getContentHeight());
         } else {
-            contentHeight = mContentHeight;
+            contentHeight = getContentHeight();
         }
         return Math.max(mMaxLayoutHeight - contentHeight, 0);
     }
@@ -4569,7 +4560,7 @@
                         float diff = endPosition - layoutEnd;
                         mScrollViewFields.sendSyntheticScroll(diff);
                     }
-                    setOwnScrollY((int) (mOwnScrollY + endPosition - layoutEnd));
+                    setOwnScrollY((int) (getOwnScrollY() + endPosition - layoutEnd));
                     mDisallowScrollingInThisMotion = true;
                 }
             }
@@ -5080,7 +5071,7 @@
             event.setScrollY(mScrollViewFields.getScrollState().getScrollPosition());
             event.setMaxScrollY(mScrollViewFields.getScrollState().getMaxScrollPosition());
         } else {
-            event.setScrollY(mOwnScrollY);
+            event.setScrollY(getOwnScrollY());
             event.setMaxScrollY(getScrollRange());
         }
     }
@@ -5286,8 +5277,8 @@
 
         // If notifications are scrolled,
         // clear out scrollY by the time we push notifications offscreen
-        if (mOwnScrollY > 0) {
-            setOwnScrollY((int) MathUtils.lerp(mOwnScrollY, 0, mQsExpansionFraction));
+        if (getOwnScrollY() > 0) {
+            setOwnScrollY((int) MathUtils.lerp(getOwnScrollY(), 0, mQsExpansionFraction));
         }
         if (!FooterViewRefactor.isEnabled() && footerAffected) {
             updateFooter();
@@ -5295,6 +5286,15 @@
     }
 
     @VisibleForTesting
+    int getOwnScrollY() {
+        if (SceneContainerFlag.isEnabled()) {
+            return 0;
+        } else {
+            return mOwnScrollY;
+        }
+    }
+
+    @VisibleForTesting
     void setOwnScrollY(int ownScrollY) {
         setOwnScrollY(ownScrollY, false /* animateScrollChangeListener */);
     }
@@ -5311,11 +5311,11 @@
             return;
         }
 
-        if (ownScrollY != mOwnScrollY) {
+        if (ownScrollY != getOwnScrollY()) {
             // We still want to call the normal scrolled changed for accessibility reasons
-            onScrollChanged(mScrollX, ownScrollY, mScrollX, mOwnScrollY);
+            onScrollChanged(mScrollX, ownScrollY, mScrollX, getOwnScrollY());
             mOwnScrollY = ownScrollY;
-            mAmbientState.setScrollY(mOwnScrollY);
+            mAmbientState.setScrollY(getOwnScrollY());
             updateOnScrollChange();
             updateStackPosition(animateStackYChangeListener);
         }
@@ -5324,7 +5324,7 @@
     private void updateOnScrollChange() {
         SceneContainerFlag.assertInLegacyMode();
         if (mScrollListener != null) {
-            mScrollListener.accept(mOwnScrollY);
+            mScrollListener.accept(getOwnScrollY());
         }
         updateForwardAndBackwardScrollability();
         requestChildrenUpdate();
@@ -5517,12 +5517,7 @@
             println(pw, "hideAmount", mAmbientState.getHideAmount());
             println(pw, "ambientStateSwipingUp", mAmbientState.isSwipingUp());
             println(pw, "maxDisplayedNotifications", mMaxDisplayedNotifications);
-            println(pw, "intrinsicContentHeight", mIntrinsicContentHeight);
-            println(pw, "contentHeight", mContentHeight);
             println(pw, "intrinsicPadding", mIntrinsicPadding);
-            if (!SceneContainerFlag.isEnabled()) {
-                println(pw, "topPadding", getTopPadding());
-            }
             println(pw, "bottomPadding", mBottomPadding);
             dumpRoundedRectClipping(pw);
             println(pw, "requestedClipBounds", mRequestedClipBounds);
@@ -5547,6 +5542,12 @@
             println(pw, "isSmallLandscapeLockscreenEnabled", mIsSmallLandscapeLockscreenEnabled);
             mNotificationStackSizeCalculator.dump(pw, args);
             mScrollViewFields.dump(pw);
+            if (!SceneContainerFlag.isEnabled()) {
+                // fields which will be removed with SceneContainer
+                println(pw, "intrinsicContentHeight", getIntrinsicContentHeight());
+                println(pw, "contentHeight", getContentHeight());
+                println(pw, "topPadding", getTopPadding());
+            }
         });
         pw.println();
         pw.println("Contents:");
@@ -6891,7 +6892,7 @@
         public void expansionStateChanged(boolean isExpanding) {
             mExpandingNotification = isExpanding;
             if (!mExpandedInThisMotion) {
-                mMaxScrollAfterExpand = mOwnScrollY;
+                mMaxScrollAfterExpand = getOwnScrollY();
                 mExpandedInThisMotion = true;
             }
         }
@@ -6947,4 +6948,27 @@
         void onAnimationEnd(
                 List<ExpandableNotificationRow> viewsToRemove, @SelectedRows int selectedRows);
     }
+
+    // -------------------- Getters / Setters for the SceneContainer refactor ----------------------
+
+    /** Use {@link ScrollViewFields#intrinsicStackHeight}, when SceneContainerFlag is enabled. */
+    private int getContentHeight() {
+        return mContentHeight;
+    }
+
+    private void setContentHeight(int contentHeight) {
+        mContentHeight = contentHeight;
+    }
+
+    /**
+     * Use {@link ScrollViewFields#intrinsicStackHeight}, when SceneContainerFlag is enabled.
+     * @return the height of the content ignoring the footer.
+     */
+    public float getIntrinsicContentHeight() {
+        return mIntrinsicContentHeight;
+    }
+
+    private void setIntrinsicContentHeight(float intrinsicContentHeight) {
+        mIntrinsicContentHeight = intrinsicContentHeight;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index 04974b4..dc1a191 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -43,7 +43,6 @@
 import android.provider.Settings;
 import android.service.notification.StatusBarNotification;
 import android.util.Log;
-import android.util.Pair;
 import android.util.Property;
 import android.view.Display;
 import android.view.MotionEvent;
@@ -105,6 +104,7 @@
 import com.android.systemui.statusbar.notification.LaunchAnimationParameters;
 import com.android.systemui.statusbar.notification.NotificationActivityStarter;
 import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
+import com.android.systemui.statusbar.notification.collection.EntryWithDismissStats;
 import com.android.systemui.statusbar.notification.collection.NotifCollection;
 import com.android.systemui.statusbar.notification.collection.NotifPipeline;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
@@ -1188,7 +1188,7 @@
 
     public int getIntrinsicContentHeight() {
         SceneContainerFlag.assertInLegacyMode();
-        return mView.getIntrinsicContentHeight();
+        return (int) mView.getIntrinsicContentHeight();
     }
 
     /**
@@ -1777,12 +1777,12 @@
             mNotifCollection.dismissAllNotifications(
                     mLockscreenUserManager.getCurrentUserId());
         } else {
-            final List<Pair<NotificationEntry, DismissedByUserStats>>
+            final List<EntryWithDismissStats>
                     entriesWithRowsDismissedFromShade = new ArrayList<>();
             for (ExpandableNotificationRow row : viewsToRemove) {
                 final NotificationEntry entry = row.getEntry();
                 entriesWithRowsDismissedFromShade.add(
-                        new Pair<>(entry, getDismissedByUserStats(entry)));
+                        new EntryWithDismissStats(entry, getDismissedByUserStats(entry)));
             }
             mNotifCollection.dismissNotifications(entriesWithRowsDismissedFromShade);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractor.kt
index 6042964..e644815 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractor.kt
@@ -25,7 +25,7 @@
 import com.android.systemui.res.R
 import com.android.systemui.scene.shared.flag.SceneContainerFlag
 import com.android.systemui.shade.LargeScreenHeaderHelper
-import com.android.systemui.shade.domain.interactor.ShadeInteractor
+import com.android.systemui.shade.ShadeDisplayAware
 import com.android.systemui.statusbar.policy.SplitShadeStateController
 import dagger.Lazy
 import javax.inject.Inject
@@ -45,9 +45,8 @@
 class SharedNotificationContainerInteractor
 @Inject
 constructor(
-    private val context: Context,
+    @ShadeDisplayAware private val context: Context,
     private val splitShadeStateController: Lazy<SplitShadeStateController>,
-    private val shadeInteractor: Lazy<ShadeInteractor>,
     configurationInteractor: ConfigurationInteractor,
     keyguardInteractor: KeyguardInteractor,
     deviceEntryUdfpsInteractor: DeviceEntryUdfpsInteractor,
@@ -66,23 +65,14 @@
      * distinctUntilChanged() to this would cause configurationBasedDimensions to miss configuration
      * updates that affect other resources, like margins or the large screen header flag.
      */
-    private val dimensionsUpdateEventsWithShouldUseSplitShade: Flow<Boolean> =
-        if (SceneContainerFlag.isEnabled) {
-            combine(
-                configurationInteractor.onAnyConfigurationChange,
-                shadeInteractor.get().isShadeLayoutWide,
-            ) { _, isShadeLayoutWide ->
-                isShadeLayoutWide
-            }
-        } else {
-            configurationInteractor.onAnyConfigurationChange.map {
-                splitShadeStateController.get().shouldUseSplitNotificationShade(context.resources)
-            }
-        }
-
+    @Deprecated("Use SharedNotificationContainerViewModel.ConfigurationBasedDimensions instead")
     val configurationBasedDimensions: Flow<ConfigurationBasedDimensions> =
-        dimensionsUpdateEventsWithShouldUseSplitShade
-            .map { shouldUseSplitShade ->
+        configurationInteractor.onAnyConfigurationChange
+            .map {
+                val shouldUseSplitShade =
+                    splitShadeStateController
+                        .get()
+                        .shouldUseSplitNotificationShade(context.resources)
                 with(context.resources) {
                     ConfigurationBasedDimensions(
                         useSplitShade = shouldUseSplitShade,
@@ -101,6 +91,10 @@
                 }
             }
             .distinctUntilChanged()
+        get() {
+            SceneContainerFlag.assertInLegacyMode()
+            return field
+        }
 
     /**
      * The notification shelf can extend over the lock icon area if:
@@ -125,6 +119,7 @@
         _notificationStackChanged.value = _notificationStackChanged.value + 1
     }
 
+    @Deprecated("Use SharedNotificationContainerViewModel.ConfigurationBasedDimensions instead")
     data class ConfigurationBasedDimensions(
         val useSplitShade: Boolean,
         val useLargeScreenHeader: Boolean,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt
index 787ff02..f75c89a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt
@@ -39,6 +39,7 @@
 import com.android.systemui.statusbar.notification.emptyshade.ui.viewbinder.EmptyShadeViewBinder
 import com.android.systemui.statusbar.notification.emptyshade.ui.viewmodel.EmptyShadeViewModel
 import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor
+import com.android.systemui.statusbar.notification.footer.shared.NotifRedesignFooter
 import com.android.systemui.statusbar.notification.footer.ui.view.FooterView
 import com.android.systemui.statusbar.notification.footer.ui.viewbinder.FooterViewBinder
 import com.android.systemui.statusbar.notification.footer.ui.viewmodel.FooterViewModel
@@ -145,7 +146,9 @@
             // The footer needs to be re-inflated every time the theme or the font size changes.
             configuration
                 .inflateLayout<FooterView>(
-                    R.layout.status_bar_notification_footer,
+                    if (NotifRedesignFooter.isEnabled)
+                        R.layout.status_bar_notification_footer_redesign
+                    else R.layout.status_bar_notification_footer,
                     parentView,
                     attachToRoot = false,
                 )
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/SharedNotificationContainerBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/SharedNotificationContainerBinder.kt
index 54b5ca3..ce89d78 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/SharedNotificationContainerBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/SharedNotificationContainerBinder.kt
@@ -18,10 +18,12 @@
 
 import androidx.lifecycle.Lifecycle
 import androidx.lifecycle.repeatOnLifecycle
+import com.android.systemui.Flags
 import com.android.systemui.common.ui.view.onLayoutChanged
 import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
 import com.android.systemui.keyguard.ui.viewmodel.ViewStateAccessor
 import com.android.systemui.lifecycle.repeatWhenAttached
 import com.android.systemui.scene.shared.flag.SceneContainerFlag
@@ -48,8 +50,19 @@
     private val notificationScrollViewBinder: NotificationScrollViewBinder,
     private val communalSettingsInteractor: CommunalSettingsInteractor,
     @Main private val mainImmediateDispatcher: CoroutineDispatcher,
+    val keyguardInteractor: KeyguardInteractor,
 ) {
 
+    private val calculateMaxNotifications: (Float, Boolean) -> Int = { space, extraShelfSpace ->
+        val shelfHeight = controller.getShelfHeight().toFloat()
+        notificationStackSizeCalculator.computeMaxKeyguardNotifications(
+            controller.view,
+            space,
+            if (extraShelfSpace) shelfHeight else 0f,
+            shelfHeight,
+        )
+    }
+
     fun bind(
         view: SharedNotificationContainer,
         viewModel: SharedNotificationContainerViewModel,
@@ -107,17 +120,9 @@
                     }
 
                     launch {
-                        viewModel
-                            .getMaxNotifications { space, extraShelfSpace ->
-                                val shelfHeight = controller.getShelfHeight().toFloat()
-                                notificationStackSizeCalculator.computeMaxKeyguardNotifications(
-                                    controller.getView(),
-                                    space,
-                                    if (extraShelfSpace) shelfHeight else 0f,
-                                    shelfHeight,
-                                )
-                            }
-                            .collect { controller.setMaxDisplayedNotifications(it) }
+                        viewModel.getMaxNotifications(calculateMaxNotifications).collect {
+                            controller.setMaxDisplayedNotifications(it)
+                        }
                     }
 
                     if (!SceneContainerFlag.isEnabled) {
@@ -136,6 +141,30 @@
                         }
                     }
 
+                    if (!SceneContainerFlag.isEnabled) {
+                        if (Flags.magicPortraitWallpapers()) {
+                            launch {
+                                viewModel
+                                    .getNotificationStackAbsoluteBottom(
+                                        calculateMaxNotifications = calculateMaxNotifications,
+                                        calculateHeight = { maxNotifications ->
+                                            notificationStackSizeCalculator.computeHeight(
+                                                maxNotifs = maxNotifications,
+                                                shelfHeight = controller.getShelfHeight().toFloat(),
+                                                stack = controller.view,
+                                            )
+                                        },
+                                        controller.getShelfHeight().toFloat(),
+                                    )
+                                    .collect { bottom ->
+                                        keyguardInteractor.setNotificationStackAbsoluteBottom(
+                                            bottom
+                                        )
+                                    }
+                            }
+                        }
+                    }
+
                     launch { viewModel.translationX.collect { x -> controller.translationX = x } }
 
                     launch {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationLockscreenScrimViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationLockscreenScrimViewModel.kt
index 84aa997..d68f769 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationLockscreenScrimViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationLockscreenScrimViewModel.kt
@@ -16,14 +16,31 @@
 
 package com.android.systemui.statusbar.notification.stack.ui.viewmodel
 
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.ReadOnlyComposable
+import androidx.compose.runtime.getValue
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.res.colorResource
+import com.android.app.tracing.coroutines.coroutineScopeTraced
+import com.android.compose.animation.scene.ElementKey
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.lifecycle.ExclusiveActivatable
+import com.android.systemui.lifecycle.Hydrator
+import com.android.systemui.notifications.ui.composable.Notifications
+import com.android.systemui.res.R
 import com.android.systemui.shade.domain.interactor.ShadeInteractor
+import com.android.systemui.shade.shared.model.ShadeMode
+import com.android.systemui.shade.ui.composable.Shade
 import com.android.systemui.statusbar.notification.stack.domain.interactor.NotificationStackAppearanceInteractor
 import com.android.systemui.util.kotlin.ActivatableFlowDumper
 import com.android.systemui.util.kotlin.ActivatableFlowDumperImpl
 import dagger.assisted.AssistedFactory
 import dagger.assisted.AssistedInject
+import kotlinx.coroutines.awaitCancellation
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.launch
 
 class NotificationLockscreenScrimViewModel
 @AssistedInject
@@ -35,7 +52,17 @@
     ActivatableFlowDumper by ActivatableFlowDumperImpl(dumpManager, "NotificationScrollViewModel"),
     ExclusiveActivatable() {
 
-    val shadeMode = shadeInteractor.shadeMode
+    private val hydrator = Hydrator("NotificationLockscreenScrimViewModel.hydrator")
+
+    val shadeMode: StateFlow<ShadeMode> = shadeInteractor.shadeMode
+
+    /** The [ElementKey] to use for the scrim. */
+    val element: ElementViewModel by
+        hydrator.hydratedStateOf(
+            traceName = "elementKey",
+            initialValue = element(shadeMode.value),
+            source = shadeMode.map { element(it) },
+        )
 
     /** Sets the alpha to apply to the NSSL for fade-in on lockscreen */
     fun setAlphaForLockscreenFadeIn(alpha: Float) {
@@ -43,11 +70,56 @@
     }
 
     override suspend fun onActivated(): Nothing {
-        activateFlowDumper()
+        coroutineScopeTraced("NotificationLockscreenScrimViewModel") {
+            launch { activateFlowDumper() }
+            launch { hydrator.activate() }
+            awaitCancellation()
+        }
     }
 
+    private fun element(shadeMode: ShadeMode): ElementViewModel {
+        return if (shadeMode == ShadeMode.Single) {
+            ElementViewModel(
+                key = Notifications.Elements.NotificationScrim,
+                color = { SingleShadeBackground },
+            )
+        } else {
+            ElementViewModel(
+                key = Shade.Elements.BackgroundScrim,
+                color = { isBouncerToLockscreen ->
+                    if (isBouncerToLockscreen) {
+                        SplitShadeBouncerToLockscreenBackground
+                    } else {
+                        SplitShadeDefaultBackground
+                    }
+                },
+            )
+        }
+    }
+
+    /** Models the UI state of the scrim. */
+    data class ElementViewModel(
+        /** The [ElementKey] to use with an `element` modifier. */
+        val key: ElementKey,
+        /** A function that returns the color to use within a `background` modifier. */
+        val color: @Composable (isBouncerToLockscreen: Boolean) -> Color,
+    )
+
     @AssistedFactory
     interface Factory {
         fun create(): NotificationLockscreenScrimViewModel
     }
+
+    companion object {
+        private val SingleShadeBackground: Color
+            @Composable @ReadOnlyComposable get() = MaterialTheme.colorScheme.surface
+
+        private val SplitShadeBouncerToLockscreenBackground: Color
+            @Composable @ReadOnlyComposable get() = MaterialTheme.colorScheme.surface
+
+        private val SplitShadeDefaultBackground: Color
+            @Composable
+            @ReadOnlyComposable
+            get() = colorResource(R.color.shade_scrim_background_dark)
+    }
 }
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 9515029..e6663d5 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
@@ -19,9 +19,11 @@
 
 package com.android.systemui.statusbar.notification.stack.ui.viewmodel
 
+import android.content.Context
 import androidx.annotation.VisibleForTesting
 import com.android.app.tracing.coroutines.flow.flowName
 import com.android.systemui.common.shared.model.NotificationContainerBounds
+import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
 import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
@@ -66,10 +68,14 @@
 import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel
 import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToLockscreenTransitionViewModel
 import com.android.systemui.keyguard.ui.viewmodel.ViewStateAccessor
+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.LargeScreenHeaderHelper
 import com.android.systemui.shade.domain.interactor.ShadeInteractor
-import com.android.systemui.shade.shared.flag.DualShade
+import com.android.systemui.shade.shared.model.ShadeMode.Dual
+import com.android.systemui.shade.shared.model.ShadeMode.Single
+import com.android.systemui.shade.shared.model.ShadeMode.Split
 import com.android.systemui.statusbar.notification.domain.interactor.HeadsUpNotificationInteractor
 import com.android.systemui.statusbar.notification.stack.domain.interactor.NotificationStackAppearanceInteractor
 import com.android.systemui.statusbar.notification.stack.domain.interactor.SharedNotificationContainerInteractor
@@ -90,6 +96,7 @@
 import kotlinx.coroutines.flow.combineTransform
 import kotlinx.coroutines.flow.distinctUntilChanged
 import kotlinx.coroutines.flow.emptyFlow
+import kotlinx.coroutines.flow.filter
 import kotlinx.coroutines.flow.first
 import kotlinx.coroutines.flow.flatMapLatest
 import kotlinx.coroutines.flow.flow
@@ -108,6 +115,8 @@
     private val interactor: SharedNotificationContainerInteractor,
     dumpManager: DumpManager,
     @Application applicationScope: CoroutineScope,
+    private val context: Context,
+    configurationInteractor: ConfigurationInteractor,
     private val keyguardInteractor: KeyguardInteractor,
     private val keyguardTransitionInteractor: KeyguardTransitionInteractor,
     private val shadeInteractor: ShadeInteractor,
@@ -145,6 +154,7 @@
     private val communalSceneInteractor: CommunalSceneInteractor,
     // Lazy because it's only used in the SceneContainer + Dual Shade configuration.
     headsUpNotificationInteractor: Lazy<HeadsUpNotificationInteractor>,
+    private val largeScreenHeaderHelperLazy: Lazy<LargeScreenHeaderHelper>,
     unfoldTransitionInteractor: UnfoldTransitionInteractor,
 ) : FlowDumperImpl(dumpManager) {
 
@@ -187,33 +197,62 @@
 
     @VisibleForTesting
     val paddingTopDimen: Flow<Int> =
-        interactor.configurationBasedDimensions
-            .map {
-                when {
-                    it.useLargeScreenHeader -> it.marginTopLargeScreen
-                    else -> it.marginTop
+        if (SceneContainerFlag.isEnabled) {
+                configurationInteractor.onAnyConfigurationChange.map {
+                    with(context.resources) {
+                        val useLargeScreenHeader =
+                            getBoolean(R.bool.config_use_large_screen_shade_header)
+                        if (useLargeScreenHeader) {
+                            largeScreenHeaderHelperLazy.get().getLargeScreenHeaderHeight()
+                        } else {
+                            getDimensionPixelSize(R.dimen.notification_panel_margin_top)
+                        }
+                    }
+                }
+            } else {
+                interactor.configurationBasedDimensions.map {
+                    when {
+                        it.useLargeScreenHeader -> it.marginTopLargeScreen
+                        else -> it.marginTop
+                    }
                 }
             }
             .distinctUntilChanged()
             .dumpWhileCollecting("paddingTopDimen")
 
     val configurationBasedDimensions: Flow<ConfigurationBasedDimensions> =
-        interactor.configurationBasedDimensions
-            .map {
-                val marginTop =
-                    when {
-                        // y position of the NSSL in the window needs to be 0 under scene container
-                        SceneContainerFlag.isEnabled -> 0
-                        it.useLargeScreenHeader -> it.marginTopLargeScreen
-                        else -> it.marginTop
+        if (SceneContainerFlag.isEnabled) {
+                combine(
+                    shadeInteractor.isShadeLayoutWide,
+                    configurationInteractor.onAnyConfigurationChange,
+                ) { isShadeLayoutWide, _ ->
+                    with(context.resources) {
+                        // TODO(b/338033836): Define separate horizontal margins for dual shade.
+                        val marginHorizontal =
+                            getDimensionPixelSize(R.dimen.notification_panel_margin_horizontal)
+                        ConfigurationBasedDimensions(
+                            marginStart = if (isShadeLayoutWide) 0 else marginHorizontal,
+                            marginEnd = marginHorizontal,
+                            marginBottom =
+                                getDimensionPixelSize(R.dimen.notification_panel_margin_bottom),
+                            // y position of the NSSL in the window needs to be 0 under scene
+                            // container
+                            marginTop = 0,
+                            useSplitShade = isShadeLayoutWide,
+                        )
                     }
-                ConfigurationBasedDimensions(
-                    marginStart = if (it.useSplitShade) 0 else it.marginHorizontal,
-                    marginEnd = it.marginHorizontal,
-                    marginBottom = it.marginBottom,
-                    marginTop = marginTop,
-                    useSplitShade = it.useSplitShade,
-                )
+                }
+            } else {
+                interactor.configurationBasedDimensions.map {
+                    ConfigurationBasedDimensions(
+                        marginStart = if (it.useSplitShade) 0 else it.marginHorizontal,
+                        marginEnd = it.marginHorizontal,
+                        marginBottom = it.marginBottom,
+                        marginTop =
+                            if (it.useLargeScreenHeader) it.marginTopLargeScreen else it.marginTop,
+                        useSplitShade = it.useSplitShade,
+                    )
+                }
             }
             .distinctUntilChanged()
             .dumpWhileCollecting("configurationBasedDimensions")
@@ -221,13 +260,15 @@
     /** If the user is visually on one of the unoccluded lockscreen states. */
     val isOnLockscreen: Flow<Boolean> =
         anyOf(
-                keyguardTransitionInteractor.isFinishedIn(AOD),
-                keyguardTransitionInteractor.isFinishedIn(DOZING),
-                keyguardTransitionInteractor.isFinishedIn(ALTERNATE_BOUNCER),
-                keyguardTransitionInteractor.isFinishedIn(
-                    scene = Scenes.Bouncer,
-                    stateWithoutSceneContainer = PRIMARY_BOUNCER,
-                ),
+                keyguardTransitionInteractor.transitionValue(AOD).map { it > 0f },
+                keyguardTransitionInteractor.transitionValue(DOZING).map { it > 0f },
+                keyguardTransitionInteractor.transitionValue(ALTERNATE_BOUNCER).map { it > 0f },
+                keyguardTransitionInteractor
+                    .transitionValue(
+                        scene = Scenes.Bouncer,
+                        stateWithoutSceneContainer = PRIMARY_BOUNCER,
+                    )
+                    .map { it > 0f },
                 keyguardTransitionInteractor.transitionValue(LOCKSCREEN).map { it > 0f },
             )
             .flowName("isOnLockscreen")
@@ -404,42 +445,60 @@
      * notifications unless in splitshade.
      */
     private val alphaForShadeAndQsExpansion: Flow<Float> =
-        if (DualShade.isEnabled) {
-                combineTransform(
-                    headsUpNotificationInteractor.get().isHeadsUpOrAnimatingAway,
-                    shadeInteractor.shadeExpansion,
-                    shadeInteractor.qsExpansion,
-                ) { isHeadsUpOrAnimatingAway, shadeExpansion, qsExpansion ->
-                    if (isHeadsUpOrAnimatingAway) {
-                        // Ensure HUNs will be visible in QS shade (at least while unlocked)
-                        emit(1f)
-                    } else if (shadeExpansion > 0f || qsExpansion > 0f) {
-                        // Fade out as QS shade expands
-                        emit(1f - qsExpansion)
-                    }
-                }
-            } else {
-                interactor.configurationBasedDimensions.flatMapLatest { configurationBasedDimensions
-                    ->
-                    combineTransform(shadeInteractor.shadeExpansion, shadeInteractor.qsExpansion) {
-                        shadeExpansion,
-                        qsExpansion ->
-                        if (shadeExpansion > 0f || qsExpansion > 0f) {
-                            if (configurationBasedDimensions.useSplitShade) {
-                                emit(1f)
-                            } else if (qsExpansion == 1f) {
+        if (SceneContainerFlag.isEnabled) {
+            shadeInteractor.shadeMode.flatMapLatest { shadeMode ->
+                when (shadeMode) {
+                    Single ->
+                        combineTransform(
+                            shadeInteractor.shadeExpansion,
+                            shadeInteractor.qsExpansion,
+                        ) { shadeExpansion, qsExpansion ->
+                            if (qsExpansion == 1f) {
                                 // Ensure HUNs will be visible in QS shade (at least while unlocked)
                                 emit(1f)
-                            } else {
+                            } else if (shadeExpansion > 0f || qsExpansion > 0f) {
                                 // Fade as QS shade expands
                                 emit(1f - qsExpansion)
                             }
                         }
+                    Split -> isAnyExpanded.filter { it }.map { 1f }
+                    Dual ->
+                        combineTransform(
+                            headsUpNotificationInteractor.get().isHeadsUpOrAnimatingAway,
+                            shadeInteractor.shadeExpansion,
+                            shadeInteractor.qsExpansion,
+                        ) { isHeadsUpOrAnimatingAway, shadeExpansion, qsExpansion ->
+                            if (isHeadsUpOrAnimatingAway) {
+                                // Ensure HUNs will be visible in QS shade (at least while unlocked)
+                                emit(1f)
+                            } else if (shadeExpansion > 0f || qsExpansion > 0f) {
+                                // Fade out as QS shade expands
+                                emit(1f - qsExpansion)
+                            }
+                        }
+                }
+            }
+        } else {
+            interactor.configurationBasedDimensions.flatMapLatest { configurationBasedDimensions ->
+                combineTransform(shadeInteractor.shadeExpansion, shadeInteractor.qsExpansion) {
+                    shadeExpansion,
+                    qsExpansion ->
+                    if (shadeExpansion > 0f || qsExpansion > 0f) {
+                        if (configurationBasedDimensions.useSplitShade) {
+                            emit(1f)
+                        } else if (qsExpansion == 1f) {
+                            // Ensure HUNs will be visible in QS shade (at least while unlocked)
+                            emit(1f)
+                        } else {
+                            // Fade as QS shade expands
+                            emit(1f - qsExpansion)
+                        }
                     }
                 }
             }
-            .onStart { emit(1f) }
-            .dumpWhileCollecting("alphaForShadeAndQsExpansion")
+        }
+        .onStart { emit(1f) }
+        .dumpWhileCollecting("alphaForShadeAndQsExpansion")
 
     val panelAlpha = keyguardInteractor.panelAlpha
 
@@ -672,6 +731,36 @@
             .dumpWhileCollecting("maxNotifications")
     }
 
+    /**
+     * Wallpaper needs the absolute bottom of notification stack to avoid occlusion
+     *
+     * @param calculateMaxNotifications is required by getMaxNotifications as calculateSpace by
+     *   calling computeMaxKeyguardNotifications in NotificationStackSizeCalculator
+     * @param calculateHeight is calling computeHeight in NotificationStackSizeCalculator The edge
+     *   case is that when maxNotifications is 0, we won't take shelfHeight into account
+     */
+    fun getNotificationStackAbsoluteBottom(
+        calculateMaxNotifications: (Float, Boolean) -> Int,
+        calculateHeight: (Int) -> Float,
+        shelfHeight: Float,
+    ): Flow<Float> {
+        SceneContainerFlag.assertInLegacyMode()
+
+        return combine(
+            getMaxNotifications(calculateMaxNotifications).map {
+                val height = calculateHeight(it)
+                if (it == 0) {
+                    height - shelfHeight
+                } else {
+                    height
+                }
+            },
+            bounds.map { it.top },
+        ) { height, top ->
+            top + height
+        }
+    }
+
     fun notificationStackChanged() {
         interactor.notificationStackChanged()
     }
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 b469e3d..99efba4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -646,6 +646,8 @@
             NavigationBarController navigationBarController,
             AccessibilityFloatingMenuController accessibilityFloatingMenuController,
             Lazy<AssistManager> assistManagerLazy,
+            // TODO: b/374267505 - Decouple the config change needed for shade window classes from
+            //  the one for other windows.
             ConfigurationController configurationController,
             NotificationShadeWindowController notificationShadeWindowController,
             Lazy<NotificationShadeWindowViewController> notificationShadeWindowViewControllerLazy,
@@ -1103,7 +1105,7 @@
         mJavaAdapter.alwaysCollectFlow(
                 mCommunalInteractor.isIdleOnCommunal(),
                 mIdleOnCommunalConsumer);
-        if (SceneContainerFlag.isEnabled()) {
+        if (SceneContainerFlag.isEnabled() || QSComposeFragment.isEnabled()) {
             mJavaAdapter.alwaysCollectFlow(
                     mBrightnessMirrorShowingInteractor.isShowing(),
                     this::setBrightnessMirrorShowing
@@ -1975,6 +1977,10 @@
      * meantime, just update the things that we know change.
      */
     void updateResources() {
+        // TODO: b/374267505 - we shouldn't propagate this from here. Each class should be
+        //  listening at the correct configuration change. For example, shade window classes should
+        //  be listening at @ShadeDisplayAware configurations (as it can be on a different display.
+
         // Update the quick setting tiles
         if (mQSPanelController != null) {
             mQSPanelController.updateResources();
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 dc4d66d..e7c6fb4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -446,6 +446,7 @@
                             mStatusBarKeyguardViewManager.onKeyguardFadedAway();
                         }
                         dispatchScrimsVisible();
+                        dispatchBackScrimState(mScrimBehind.getViewAlpha());
                     }
                 };
 
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 92b609e..9cda199 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -59,6 +59,7 @@
 import com.android.systemui.animation.back.FlingOnBackAnimationCallback;
 import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor;
 import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor;
+import com.android.systemui.bouncer.domain.interactor.BouncerInteractor;
 import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerCallbackInteractor;
 import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerCallbackInteractor.PrimaryBouncerExpansionCallback;
 import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor;
@@ -165,6 +166,7 @@
     private final PrimaryBouncerCallbackInteractor mPrimaryBouncerCallbackInteractor;
     private final PrimaryBouncerInteractor mPrimaryBouncerInteractor;
     private final AlternateBouncerInteractor mAlternateBouncerInteractor;
+    private final Lazy<BouncerInteractor> mBouncerInteractor;
     private final BouncerView mPrimaryBouncerView;
     private final Lazy<ShadeController> mShadeController;
     private final Lazy<SceneInteractor> mSceneInteractorLazy;
@@ -252,6 +254,9 @@
 
         @Override
         public void onBackProgressedCompat(@NonNull BackEvent event) {
+            if (ComposeBouncerFlags.INSTANCE.isOnlyComposeBouncerEnabled()) {
+                mBouncerInteractor.get().onBackEventProgressed(event.getProgress());
+            }
             if (shouldPlayBackAnimation() && mPrimaryBouncerView.getDelegate() != null) {
                 mPrimaryBouncerView.getDelegate().getBackCallback().onBackProgressed(event);
             }
@@ -259,6 +264,9 @@
 
         @Override
         public void onBackCancelledCompat() {
+            if (ComposeBouncerFlags.INSTANCE.isOnlyComposeBouncerEnabled()) {
+                mBouncerInteractor.get().onBackEventCancelled();
+            }
             if (shouldPlayBackAnimation() && mPrimaryBouncerView.getDelegate() != null) {
                 mPrimaryBouncerView.getDelegate().getBackCallback().onBackCancelled();
             }
@@ -400,7 +408,8 @@
             StatusBarKeyguardViewManagerInteractor statusBarKeyguardViewManagerInteractor,
             @Main DelayableExecutor executor,
             Lazy<DeviceEntryInteractor> deviceEntryInteractorLazy,
-            DismissCallbackRegistry dismissCallbackRegistry
+            DismissCallbackRegistry dismissCallbackRegistry,
+            Lazy<BouncerInteractor> bouncerInteractor
     ) {
         mContext = context;
         mExecutor = executor;
@@ -424,6 +433,7 @@
         mFoldAodAnimationController = sysUIUnfoldComponent
                 .map(SysUIUnfoldComponent::getFoldAodAnimationController).orElse(null);
         mAlternateBouncerInteractor = alternateBouncerInteractor;
+        mBouncerInteractor = bouncerInteractor;
         mIsBackAnimationEnabled = predictiveBackAnimateBouncer();
         mUdfpsOverlayInteractor = udfpsOverlayInteractor;
         mActivityStarter = activityStarter;
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 200f080..1dc9de4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java
@@ -216,10 +216,19 @@
                 if (row.isChildInGroup() && !row.areChildrenExpanded()) {
                     // The group isn't expanded, let's make sure it's visible!
                     mGroupExpansionManager.toggleGroupExpansion(row.getEntry());
-                } else if (!row.isChildInGroup() && !row.isExpanded()) {
-                    // notification isn't expanded, let's make sure it's visible!
-                    row.toggleExpansionState();
-                    row.getPrivateLayout().setOnExpandedVisibleListener(runnable);
+                } else if (!row.isChildInGroup()) {
+                    final boolean expandNotification;
+                    if (row.isPinned()) {
+                        expandNotification = !row.isPinnedAndExpanded();
+                    } else {
+                        expandNotification = !row.isExpanded();
+                    }
+
+                    if (expandNotification) {
+                        // notification isn't expanded, let's make sure it's expanded!
+                        row.toggleExpansionState();
+                        row.getPrivateLayout().setOnExpandedVisibleListener(runnable);
+                    }
                 }
             } else {
                 if (row.isChildInGroup() && !row.areChildrenExpanded()) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.kt
index 99f25bd..e4a75be 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.kt
@@ -31,6 +31,7 @@
 import com.android.systemui.statusbar.core.StatusBarInitializerStore
 import com.android.systemui.statusbar.core.StatusBarOrchestrator
 import com.android.systemui.statusbar.core.StatusBarSimpleFragment
+import com.android.systemui.statusbar.data.repository.PrivacyDotViewControllerStoreModule
 import com.android.systemui.statusbar.data.repository.StatusBarModeRepositoryStore
 import com.android.systemui.statusbar.events.PrivacyDotViewControllerModule
 import com.android.systemui.statusbar.phone.CentralSurfacesCommandQueueCallbacks
@@ -46,7 +47,9 @@
 import kotlinx.coroutines.CoroutineScope
 
 /** Similar in purpose to [StatusBarModule], but scoped only to phones */
-@Module(includes = [PrivacyDotViewControllerModule::class])
+@Module(
+    includes = [PrivacyDotViewControllerModule::class, PrivacyDotViewControllerStoreModule::class]
+)
 interface StatusBarPhoneModule {
 
     @Binds
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java
index 00116aa..31cae79 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java
@@ -287,8 +287,9 @@
 
     @Override
     public boolean isKeyguardScreenRotationAllowed() {
-        return SystemProperties.getBoolean("lockscreen.rot_override", false)
-                || mContext.getResources().getBoolean(R.bool.config_enableLockScreenRotation)
+        final boolean configEnabled =
+                mContext.getResources().getBoolean(R.bool.config_enableLockScreenRotation);
+        return SystemProperties.getBoolean("lockscreen.rot_override", configEnabled)
                 || mFeatureFlags.isEnabled(LOCKSCREEN_ENABLE_LANDSCAPE);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/TutorialSelectionScreen.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/TutorialSelectionScreen.kt
index c209311..d371acf 100644
--- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/TutorialSelectionScreen.kt
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/TutorialSelectionScreen.kt
@@ -16,15 +16,16 @@
 
 package com.android.systemui.touchpad.tutorial.ui.composable
 
+import android.content.res.Configuration
 import androidx.compose.foundation.background
 import androidx.compose.foundation.layout.Arrangement
 import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.Row
 import androidx.compose.foundation.layout.Spacer
-import androidx.compose.foundation.layout.aspectRatio
 import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.foundation.layout.height
 import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.safeDrawingPadding
 import androidx.compose.foundation.layout.width
 import androidx.compose.foundation.shape.RoundedCornerShape
 import androidx.compose.material.icons.Icons
@@ -40,6 +41,7 @@
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.vector.ImageVector
 import androidx.compose.ui.input.pointer.pointerInteropFilter
+import androidx.compose.ui.platform.LocalConfiguration
 import androidx.compose.ui.res.stringResource
 import androidx.compose.ui.res.vectorResource
 import androidx.compose.ui.unit.dp
@@ -60,6 +62,7 @@
         modifier =
             Modifier.background(color = MaterialTheme.colorScheme.surfaceContainer)
                 .fillMaxSize()
+                .safeDrawingPadding()
                 .pointerInteropFilter(
                     onTouchEvent = { event ->
                         // Because of window flag we're intercepting 3 and 4-finger swipes.
@@ -69,12 +72,26 @@
                     }
                 ),
     ) {
-        TutorialSelectionButtons(
-            onBackTutorialClicked = onBackTutorialClicked,
-            onHomeTutorialClicked = onHomeTutorialClicked,
-            onRecentAppsTutorialClicked = onRecentAppsTutorialClicked,
-            modifier = Modifier.padding(60.dp),
-        )
+        val configuration = LocalConfiguration.current
+        when (configuration.orientation) {
+            Configuration.ORIENTATION_LANDSCAPE -> {
+                HorizontalSelectionButtons(
+                    onBackTutorialClicked = onBackTutorialClicked,
+                    onHomeTutorialClicked = onHomeTutorialClicked,
+                    onRecentAppsTutorialClicked = onRecentAppsTutorialClicked,
+                    modifier = Modifier.weight(1f).padding(60.dp),
+                )
+            }
+            else -> {
+                VerticalSelectionButtons(
+                    onBackTutorialClicked = onBackTutorialClicked,
+                    onHomeTutorialClicked = onHomeTutorialClicked,
+                    onRecentAppsTutorialClicked = onRecentAppsTutorialClicked,
+                    modifier = Modifier.weight(1f).padding(60.dp),
+                )
+            }
+        }
+        // because other composables have weight 1, Done button will be positioned first
         DoneButton(
             onDoneButtonClicked = onDoneButtonClicked,
             modifier = Modifier.padding(horizontal = 60.dp),
@@ -83,7 +100,7 @@
 }
 
 @Composable
-private fun TutorialSelectionButtons(
+private fun HorizontalSelectionButtons(
     onBackTutorialClicked: () -> Unit,
     onHomeTutorialClicked: () -> Unit,
     onRecentAppsTutorialClicked: () -> Unit,
@@ -94,34 +111,70 @@
         verticalAlignment = Alignment.CenterVertically,
         modifier = modifier,
     ) {
-        TutorialButton(
-            text = stringResource(R.string.touchpad_tutorial_home_gesture_button),
-            icon = ImageVector.vectorResource(id = R.drawable.touchpad_tutorial_home_icon),
-            iconColor = MaterialTheme.colorScheme.onPrimary,
-            onClick = onHomeTutorialClicked,
-            backgroundColor = MaterialTheme.colorScheme.primary,
-            modifier = Modifier.weight(1f),
-        )
-        TutorialButton(
-            text = stringResource(R.string.touchpad_tutorial_back_gesture_button),
-            icon = Icons.AutoMirrored.Outlined.ArrowBack,
-            iconColor = MaterialTheme.colorScheme.onTertiary,
-            onClick = onBackTutorialClicked,
-            backgroundColor = MaterialTheme.colorScheme.tertiary,
-            modifier = Modifier.weight(1f),
-        )
-        TutorialButton(
-            text = stringResource(R.string.touchpad_tutorial_recent_apps_gesture_button),
-            icon = ImageVector.vectorResource(id = R.drawable.touchpad_tutorial_recents_icon),
-            iconColor = MaterialTheme.colorScheme.onSecondary,
-            onClick = onRecentAppsTutorialClicked,
-            backgroundColor = MaterialTheme.colorScheme.secondary,
-            modifier = Modifier.weight(1f),
+        ThreeTutorialButtons(
+            onBackTutorialClicked,
+            onHomeTutorialClicked,
+            onRecentAppsTutorialClicked,
+            modifier = Modifier.weight(1f).fillMaxSize(),
         )
     }
 }
 
 @Composable
+private fun VerticalSelectionButtons(
+    onBackTutorialClicked: () -> Unit,
+    onHomeTutorialClicked: () -> Unit,
+    onRecentAppsTutorialClicked: () -> Unit,
+    modifier: Modifier = Modifier,
+) {
+    Column(
+        verticalArrangement = Arrangement.spacedBy(20.dp),
+        horizontalAlignment = Alignment.CenterHorizontally,
+        modifier = modifier,
+    ) {
+        ThreeTutorialButtons(
+            onBackTutorialClicked,
+            onHomeTutorialClicked,
+            onRecentAppsTutorialClicked,
+            modifier = Modifier.weight(1f).fillMaxSize(),
+        )
+    }
+}
+
+@Composable
+private fun ThreeTutorialButtons(
+    onBackTutorialClicked: () -> Unit,
+    onHomeTutorialClicked: () -> Unit,
+    onRecentAppsTutorialClicked: () -> Unit,
+    modifier: Modifier = Modifier,
+) {
+    TutorialButton(
+        text = stringResource(R.string.touchpad_tutorial_home_gesture_button),
+        icon = ImageVector.vectorResource(id = R.drawable.touchpad_tutorial_home_icon),
+        iconColor = MaterialTheme.colorScheme.onPrimary,
+        onClick = onHomeTutorialClicked,
+        backgroundColor = MaterialTheme.colorScheme.primary,
+        modifier = modifier,
+    )
+    TutorialButton(
+        text = stringResource(R.string.touchpad_tutorial_back_gesture_button),
+        icon = Icons.AutoMirrored.Outlined.ArrowBack,
+        iconColor = MaterialTheme.colorScheme.onTertiary,
+        onClick = onBackTutorialClicked,
+        backgroundColor = MaterialTheme.colorScheme.tertiary,
+        modifier = modifier,
+    )
+    TutorialButton(
+        text = stringResource(R.string.touchpad_tutorial_recent_apps_gesture_button),
+        icon = ImageVector.vectorResource(id = R.drawable.touchpad_tutorial_recents_icon),
+        iconColor = MaterialTheme.colorScheme.onSecondary,
+        onClick = onRecentAppsTutorialClicked,
+        backgroundColor = MaterialTheme.colorScheme.secondary,
+        modifier = modifier,
+    )
+}
+
+@Composable
 private fun TutorialButton(
     text: String,
     icon: ImageVector,
@@ -134,7 +187,7 @@
         onClick = onClick,
         shape = RoundedCornerShape(16.dp),
         colors = ButtonDefaults.buttonColors(containerColor = backgroundColor),
-        modifier = modifier.aspectRatio(0.66f),
+        modifier = modifier,
     ) {
         Column(
             verticalArrangement = Arrangement.Center,
diff --git a/packages/SystemUI/src/com/android/systemui/util/ConvenienceExtensions.kt b/packages/SystemUI/src/com/android/systemui/util/ConvenienceExtensions.kt
index f2132248..70fd5ab 100644
--- a/packages/SystemUI/src/com/android/systemui/util/ConvenienceExtensions.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/ConvenienceExtensions.kt
@@ -25,9 +25,7 @@
 
 /** [Sequence] that yields all of the direct children of this [ViewGroup] */
 val ViewGroup.children
-    get() = sequence {
-        for (i in 0 until childCount) yield(getChildAt(i))
-    }
+    get() = sequence { for (i in 0 until childCount) yield(getChildAt(i)) }
 
 /** Inclusive version of [Iterable.takeWhile] */
 fun <T> Sequence<T>.takeUntil(pred: (T) -> Boolean): Sequence<T> = sequence {
@@ -62,3 +60,25 @@
 fun <T> Lazy<T>.toKotlinLazy(): kotlin.Lazy<T> {
     return lazy { this.get() }
 }
+
+/**
+ * Returns whether this [Collection] contains exactly all [elements].
+ *
+ * Order of elements is not taken into account, but multiplicity is. For example, an element
+ * duplicated exactly 3 times in the parameter asserts that the element must likewise be duplicated
+ * exactly 3 times in this [Collection].
+ */
+fun <T> Collection<T>.containsExactly(vararg elements: T): Boolean {
+    return eachCountMap() == elements.asList().eachCountMap()
+}
+
+/**
+ * Returns a map where keys are the distinct elements of the collection and values are their
+ * corresponding counts.
+ *
+ * This is a convenient extension function for any [Collection] that allows you to easily count the
+ * occurrences of each element.
+ */
+fun <T> Collection<T>.eachCountMap(): Map<T, Int> {
+    return groupingBy { it }.eachCount()
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
index bbd8f3dc..b705872 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
@@ -552,7 +552,7 @@
                 && mShowVolumeDialog;
     }
 
-    boolean onVolumeChangedW(int stream, int flags) {
+    boolean onVolumeChangedW(int stream, int flags, boolean sendChanges) {
         final boolean showUI = shouldShowUI(flags);
         final boolean fromKey = (flags & AudioManager.FLAG_FROM_KEY) != 0;
         final boolean showVibrateHint = (flags & AudioManager.FLAG_SHOW_VIBRATE_HINT) != 0;
@@ -564,7 +564,7 @@
         int lastAudibleStreamVolume = getAudioManagerStreamVolume(stream);
         changed |= updateStreamLevelW(stream, lastAudibleStreamVolume);
         changed |= checkRoutedToBluetoothW(showUI ? AudioManager.STREAM_MUSIC : stream);
-        if (changed) {
+        if (changed && sendChanges) {
             mCallbacks.onStateChanged(mState);
         }
         if (showUI) {
@@ -950,7 +950,7 @@
         @Override
         public void handleMessage(Message msg) {
             switch (msg.what) {
-                case VOLUME_CHANGED: onVolumeChangedW(msg.arg1, msg.arg2); break;
+                case VOLUME_CHANGED: onVolumeChangedW(msg.arg1, msg.arg2, true); break;
                 case DISMISS_REQUESTED: onDismissRequestedW(msg.arg1); break;
                 case GET_STATE: onGetStateW(); break;
                 case SET_RINGER_MODE: onSetRingerModeW(msg.arg1, msg.arg2 != 0); break;
@@ -1307,7 +1307,7 @@
                 if (D.BUG) Log.d(TAG, "onReceive VOLUME_CHANGED_ACTION stream=" + stream
                         + " oldLevel=" + oldLevel);
                 if (stream != STREAM_UNKNOWN) {
-                    changed |= onVolumeChangedW(stream, 0);
+                    changed |= onVolumeChangedW(stream, 0, false);
                 }
             } else if (action.equals(AudioManager.STREAM_DEVICES_CHANGED_ACTION)) {
                 final int stream = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE,
@@ -1320,7 +1320,7 @@
                         + stream + " devices=" + devices + " oldDevices=" + oldDevices);
                 if (stream != STREAM_UNKNOWN) {
                     changed |= checkRoutedToBluetoothW(stream);
-                    changed |= onVolumeChangedW(stream, 0);
+                    changed |= onVolumeChangedW(stream, 0, false);
                 }
             } else if (action.equals(AudioManager.STREAM_MUTE_CHANGED_ACTION)) {
                 final int stream = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE,
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
index 07509e6..bd4c463 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -120,9 +120,10 @@
 import com.android.systemui.Flags;
 import com.android.systemui.Prefs;
 import com.android.systemui.dump.DumpManager;
+import com.android.systemui.haptics.slider.HapticSlider;
 import com.android.systemui.haptics.slider.HapticSliderViewBinder;
 import com.android.systemui.haptics.slider.SeekableSliderTrackerConfig;
-import com.android.systemui.haptics.slider.SeekbarHapticPlugin;
+import com.android.systemui.haptics.slider.HapticSliderPlugin;
 import com.android.systemui.haptics.slider.SliderHapticFeedbackConfig;
 import com.android.systemui.media.dialog.MediaOutputDialogManager;
 import com.android.systemui.plugins.VolumeDialog;
@@ -939,7 +940,7 @@
     }
 
     private void addSliderHapticsToRow(VolumeRow row) {
-        row.createPlugin(mVibratorHelper, mMSDLPlayer, mSystemClock);
+        row.createPlugin(row.slider, mVibratorHelper, mMSDLPlayer, mSystemClock);
         HapticSliderViewBinder.bind(row.slider, row.mHapticPlugin);
     }
 
@@ -2638,7 +2639,7 @@
             if (D.BUG) Log.d(TAG, "onStartTrackingTouch"+ " " + mRow.stream);
             Events.writeEvent(Events.EVENT_SLIDER_TOUCH_TRACKING, /* startedTracking= */true);
             if (mRow.mHapticPlugin != null) {
-                mRow.mHapticPlugin.onStartTrackingTouch(seekBar);
+                mRow.mHapticPlugin.onStartTrackingTouch();
             }
             mController.setActiveStream(mRow.stream);
             mRow.tracking = true;
@@ -2649,7 +2650,7 @@
             if (D.BUG) Log.d(TAG, "onStopTrackingTouch"+ " " + mRow.stream);
             Events.writeEvent(Events.EVENT_SLIDER_TOUCH_TRACKING, /* startedTracking= */false);
             if (mRow.mHapticPlugin != null) {
-                mRow.mHapticPlugin.onStopTrackingTouch(seekBar);
+                mRow.mHapticPlugin.onStopTrackingTouch();
             }
             mRow.tracking = false;
             mRow.userAttempt = SystemClock.uptimeMillis();
@@ -2697,7 +2698,8 @@
                 /* velocityAxis= */ MotionEvent.AXIS_Y,
                 /* upperBookendScale= */ 1f,
                 /* lowerBookendScale= */ 0.05f,
-                /* exponent= */ 1f / 0.89f);
+                /* exponent= */ 1f / 0.89f,
+                /* sliderStepSize = */ 0f);
         private static final SeekableSliderTrackerConfig sSliderTrackerConfig =
                 new SeekableSliderTrackerConfig(
                         /* waitTimeMillis= */100,
@@ -2726,7 +2728,7 @@
         private ObjectAnimator anim;  // slider progress animation for non-touch-related updates
         private int animTargetProgress;
         private int lastAudibleLevel = 1;
-        private SeekbarHapticPlugin mHapticPlugin;
+        private HapticSliderPlugin mHapticPlugin;
 
         void setIcon(int iconRes, Resources.Theme theme) {
             if (icon != null) {
@@ -2739,15 +2741,17 @@
         }
 
         void createPlugin(
+                SeekBar seekBar,
                 VibratorHelper vibratorHelper,
                 MSDLPlayer msdlPlayer,
                 com.android.systemui.util.time.SystemClock systemClock) {
             if (mHapticPlugin != null) return;
 
-            mHapticPlugin = new SeekbarHapticPlugin(
+            mHapticPlugin = new HapticSliderPlugin(
                 vibratorHelper,
                 msdlPlayer,
                 systemClock,
+                new HapticSlider.SeekBar(seekBar),
                 sSliderHapticFeedbackConfig,
                 sSliderTrackerConfig);
         }
@@ -2782,7 +2786,7 @@
         boolean deliverOnProgressChangedHaptics(boolean fromUser, int progress) {
             if (mHapticPlugin == null) return false;
 
-            mHapticPlugin.onProgressChanged(slider, progress, fromUser);
+            mHapticPlugin.onProgressChanged(progress, fromUser);
             if (!fromUser) {
                 // Consider a change from program as the volume key being continuously pressed
                 mHapticPlugin.onKeyDown();
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/VolumeDialog.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/VolumeDialog.kt
index 7476c6a..097a60f 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/VolumeDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/VolumeDialog.kt
@@ -25,20 +25,20 @@
 import com.android.systemui.res.R
 import com.android.systemui.volume.Events
 import com.android.systemui.volume.dialog.domain.interactor.VolumeDialogVisibilityInteractor
-import com.android.systemui.volume.dialog.ui.binder.VolumeDialogBinder
+import com.android.systemui.volume.dialog.ui.binder.VolumeDialogViewBinder
 import javax.inject.Inject
 
 class VolumeDialog
 @Inject
 constructor(
     @Application context: Context,
-    private val dialogBinder: VolumeDialogBinder,
+    private val viewBinder: VolumeDialogViewBinder,
     private val visibilityInteractor: VolumeDialogVisibilityInteractor,
 ) : Dialog(ContextThemeWrapper(context, R.style.volume_dialog_theme)) {
 
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
-        dialogBinder.bind(this)
+        viewBinder.bind(this)
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/VolumeDialogPlugin.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/VolumeDialogPlugin.kt
index 1a19806..b912361 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/VolumeDialogPlugin.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/VolumeDialogPlugin.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.volume.dialog
 
+import com.android.app.tracing.coroutines.launchTraced as launch
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.plugins.VolumeDialog
 import com.android.systemui.volume.dialog.dagger.VolumeDialogPluginComponent
@@ -23,7 +24,6 @@
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Job
 import kotlinx.coroutines.coroutineScope
-import com.android.app.tracing.coroutines.launchTraced as launch
 
 class VolumeDialogPlugin
 @Inject
@@ -33,19 +33,22 @@
 ) : VolumeDialog {
 
     private var job: Job? = null
+    private var pluginComponent: VolumeDialogPluginComponent? = null
 
     override fun init(windowType: Int, callback: VolumeDialog.Callback?) {
         job =
             applicationCoroutineScope.launch {
                 coroutineScope {
-                    val component = volumeDialogPluginComponentFactory.create(this)
-
-                    component.viewModel().activate()
+                    pluginComponent =
+                        volumeDialogPluginComponentFactory.create(this).also {
+                            it.viewModel().launchVolumeDialog()
+                        }
                 }
             }
     }
 
     override fun destroy() {
         job?.cancel()
+        pluginComponent = null
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogCallbacksInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogCallbacksInteractor.kt
index 3d125b8..fa108842 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogCallbacksInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogCallbacksInteractor.kt
@@ -105,8 +105,8 @@
             scope.trySend(VolumeDialogEventModel.ShowSafetyWarning(flags))
         }
 
-        override fun onAccessibilityModeChanged(showA11yStream: Boolean) {
-            scope.trySend(VolumeDialogEventModel.AccessibilityModeChanged(showA11yStream))
+        override fun onAccessibilityModeChanged(showA11yStream: Boolean?) {
+            scope.trySend(VolumeDialogEventModel.AccessibilityModeChanged(showA11yStream == true))
         }
 
         // Captions button is remove from the Volume Dialog
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogVisibilityInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogVisibilityInteractor.kt
index 2668589b..7307807 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogVisibilityInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogVisibilityInteractor.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.volume.dialog.domain.interactor
 
 import android.annotation.SuppressLint
+import com.android.systemui.plugins.VolumeDialogController
 import com.android.systemui.volume.Events
 import com.android.systemui.volume.dialog.dagger.scope.VolumeDialogPlugin
 import com.android.systemui.volume.dialog.dagger.scope.VolumeDialogPluginScope
@@ -34,11 +35,14 @@
 import kotlinx.coroutines.delay
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableSharedFlow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.filterNotNull
 import kotlinx.coroutines.flow.launchIn
 import kotlinx.coroutines.flow.mapLatest
 import kotlinx.coroutines.flow.mapNotNull
 import kotlinx.coroutines.flow.merge
 import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.flow.stateIn
 
 private val MAX_DIALOG_SHOW_TIME: Duration = 3.seconds
 
@@ -57,11 +61,16 @@
     callbacksInteractor: VolumeDialogCallbacksInteractor,
     private val tracer: VolumeTracer,
     private val repository: VolumeDialogVisibilityRepository,
+    private val controller: VolumeDialogController,
 ) {
 
     @SuppressLint("SharedFlowCreation")
-    private val mutableDismissDialogEvents = MutableSharedFlow<Unit>()
-    val dialogVisibility: Flow<VolumeDialogVisibilityModel> = repository.dialogVisibility
+    private val mutableDismissDialogEvents = MutableSharedFlow<Unit>(extraBufferCapacity = 1)
+    val dialogVisibility: Flow<VolumeDialogVisibilityModel> =
+        repository.dialogVisibility
+            .onEach { controller.notifyVisible(it is Visible) }
+            .stateIn(coroutineScope, SharingStarted.Eagerly, null)
+            .filterNotNull()
 
     init {
         merge(
@@ -74,7 +83,7 @@
             .mapNotNull { it.toVisibilityModel() }
             .onEach { model ->
                 updateVisibility { model }
-                if (model is VolumeDialogVisibilityModel.Visible) {
+                if (model is Visible) {
                     resetDismissTimeout()
                 }
             }
@@ -87,17 +96,17 @@
      */
     fun dismissDialog(reason: Int) {
         updateVisibility { visibilityModel ->
-            if (visibilityModel is VolumeDialogVisibilityModel.Dismissed) {
+            if (visibilityModel is Dismissed) {
                 visibilityModel
             } else {
-                VolumeDialogVisibilityModel.Dismissed(reason)
+                Dismissed(reason)
             }
         }
     }
 
     /** Resets current dialog timeout. */
-    suspend fun resetDismissTimeout() {
-        mutableDismissDialogEvents.emit(Unit)
+    fun resetDismissTimeout() {
+        mutableDismissDialogEvents.tryEmit(Unit)
     }
 
     private fun updateVisibility(
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/domain/VolumeDialogRingerInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/domain/VolumeDialogRingerInteractor.kt
index 7265b821..281e57f 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/domain/VolumeDialogRingerInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/domain/VolumeDialogRingerInteractor.kt
@@ -21,6 +21,7 @@
 import android.media.AudioManager.RINGER_MODE_SILENT
 import android.media.AudioManager.RINGER_MODE_VIBRATE
 import android.provider.Settings
+import com.android.settingslib.volume.data.repository.AudioSystemRepository
 import com.android.settingslib.volume.shared.model.RingerMode
 import com.android.systemui.plugins.VolumeDialogController
 import com.android.systemui.volume.dialog.dagger.scope.VolumeDialog
@@ -43,6 +44,7 @@
     @VolumeDialog private val coroutineScope: CoroutineScope,
     volumeDialogStateInteractor: VolumeDialogStateInteractor,
     private val controller: VolumeDialogController,
+    private val audioSystemRepository: AudioSystemRepository,
 ) {
 
     val ringerModel: Flow<VolumeDialogRingerModel> =
@@ -70,6 +72,7 @@
                 isMuted = it.level == 0 || it.muted,
                 level = it.level,
                 levelMax = it.levelMax,
+                isSingleVolume = audioSystemRepository.isSingleVolume,
             )
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/shared/model/VolumeDialogRingerModel.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/shared/model/VolumeDialogRingerModel.kt
index cf23f1a..3c24e02 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/shared/model/VolumeDialogRingerModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/shared/model/VolumeDialogRingerModel.kt
@@ -31,4 +31,6 @@
     val level: Int,
     /** Ring stream maximum level */
     val levelMax: Int,
+    /** in single volume mode */
+    val isSingleVolume: Boolean,
 )
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/binder/VolumeDialogRingerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/binder/VolumeDialogRingerViewBinder.kt
new file mode 100644
index 0000000..c05a0b2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/binder/VolumeDialogRingerViewBinder.kt
@@ -0,0 +1,48 @@
+/*
+ * 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.dialog.ringer.ui.binder
+
+import android.view.View
+import com.android.systemui.lifecycle.WindowLifecycleState
+import com.android.systemui.lifecycle.repeatWhenAttached
+import com.android.systemui.lifecycle.setSnapshotBinding
+import com.android.systemui.lifecycle.viewModel
+import com.android.systemui.volume.dialog.dagger.scope.VolumeDialogScope
+import com.android.systemui.volume.dialog.ringer.ui.viewmodel.VolumeDialogRingerDrawerViewModel
+import javax.inject.Inject
+import kotlinx.coroutines.awaitCancellation
+
+@VolumeDialogScope
+class VolumeDialogRingerViewBinder
+@Inject
+constructor(private val viewModelFactory: VolumeDialogRingerDrawerViewModel.Factory) {
+
+    fun bind(view: View) {
+        with(view) {
+            repeatWhenAttached {
+                viewModel(
+                    traceName = "VolumeDialogRingerViewBinder",
+                    minWindowLifecycleState = WindowLifecycleState.ATTACHED,
+                    factory = { viewModelFactory.create() },
+                ) { viewModel ->
+                    setSnapshotBinding {}
+                    awaitCancellation()
+                }
+            }
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/viewmodel/RingerButtonViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/viewmodel/RingerButtonViewModel.kt
new file mode 100644
index 0000000..78d2d16
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/viewmodel/RingerButtonViewModel.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.volume.dialog.ringer.ui.viewmodel
+
+import android.annotation.DrawableRes
+import android.annotation.StringRes
+import com.android.settingslib.volume.shared.model.RingerMode
+
+/** Models ringer button that corresponds to each ringer mode. */
+data class RingerButtonViewModel(
+    /** Image resource id for the image button. */
+    @DrawableRes val imageResId: Int,
+    /** Content description for a11y. */
+    @StringRes val contentDescriptionResId: Int,
+    /** Hint label for accessibility use. */
+    @StringRes val hintLabelResId: Int,
+    /** Used to notify view model when button is clicked. */
+    val ringerMode: RingerMode,
+)
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/viewmodel/RingerDrawerState.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/viewmodel/RingerDrawerState.kt
new file mode 100644
index 0000000..f321837
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/viewmodel/RingerDrawerState.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.dialog.ringer.ui.viewmodel
+
+import com.android.settingslib.volume.shared.model.RingerMode
+
+/** Models volume dialog ringer drawer state */
+sealed interface RingerDrawerState {
+
+    /** When clicked to open drawer */
+    data class Open(val mode: RingerMode) : RingerDrawerState
+
+    /** When clicked to close drawer */
+    data class Closed(val mode: RingerMode) : RingerDrawerState
+
+    /** Initial state when volume dialog is shown with a closed drawer. */
+    interface Initial : RingerDrawerState {
+        companion object : Initial
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/viewmodel/RingerViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/viewmodel/RingerViewModel.kt
new file mode 100644
index 0000000..a09bfeb
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/viewmodel/RingerViewModel.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume.dialog.ringer.ui.viewmodel
+
+/** Models volume dialog ringer */
+data class RingerViewModel(
+    /** List of the available buttons according to the available modes */
+    val availableButtons: List<RingerButtonViewModel?>,
+    /** The index of the currently selected button */
+    val currentButtonIndex: Int,
+    /** For open and close animations */
+    val drawerState: RingerDrawerState,
+)
diff --git a/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodRunnerState.java b/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/viewmodel/RingerViewModelState.kt
similarity index 68%
copy from ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodRunnerState.java
copy to packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/viewmodel/RingerViewModelState.kt
index 83cbc52..78b00af 100644
--- a/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodRunnerState.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/viewmodel/RingerViewModelState.kt
@@ -13,10 +13,13 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package android.platform.test.ravenwood;
 
-/** Stub class. The actual implementaetion is in junit-impl-src. */
-public class RavenwoodRunnerState {
-    public RavenwoodRunnerState(RavenwoodAwareTestRunner runner) {
-    }
+package com.android.systemui.volume.dialog.ringer.ui.viewmodel
+
+/** Models ringer view model state. */
+sealed class RingerViewModelState {
+
+    data class Available(val uiModel: RingerViewModel) : RingerViewModelState()
+
+    data object Unavailable : RingerViewModelState()
 }
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/viewmodel/VolumeDialogRingerDrawerViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/viewmodel/VolumeDialogRingerDrawerViewModel.kt
new file mode 100644
index 0000000..5b73107
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/viewmodel/VolumeDialogRingerDrawerViewModel.kt
@@ -0,0 +1,176 @@
+/*
+ * 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.dialog.ringer.ui.viewmodel
+
+import android.media.AudioAttributes
+import android.media.AudioManager.RINGER_MODE_NORMAL
+import android.media.AudioManager.RINGER_MODE_SILENT
+import android.media.AudioManager.RINGER_MODE_VIBRATE
+import android.os.VibrationEffect
+import com.android.settingslib.volume.shared.model.RingerMode
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.res.R
+import com.android.systemui.statusbar.VibratorHelper
+import com.android.systemui.volume.Events
+import com.android.systemui.volume.dialog.dagger.scope.VolumeDialog
+import com.android.systemui.volume.dialog.ringer.domain.VolumeDialogRingerInteractor
+import com.android.systemui.volume.dialog.ringer.shared.model.VolumeDialogRingerModel
+import com.android.systemui.volume.dialog.shared.VolumeDialogLogger
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.stateIn
+
+private const val TAG = "VolumeDialogRingerDrawerViewModel"
+
+class VolumeDialogRingerDrawerViewModel
+@AssistedInject
+constructor(
+    @VolumeDialog private val coroutineScope: CoroutineScope,
+    @Background private val backgroundDispatcher: CoroutineDispatcher,
+    private val interactor: VolumeDialogRingerInteractor,
+    private val vibrator: VibratorHelper,
+    private val volumeDialogLogger: VolumeDialogLogger,
+) {
+
+    private val drawerState = MutableStateFlow<RingerDrawerState>(RingerDrawerState.Initial)
+
+    val ringerViewModel: StateFlow<RingerViewModelState> =
+        combine(interactor.ringerModel, drawerState) { ringerModel, state ->
+                ringerModel.toViewModel(state)
+            }
+            .flowOn(backgroundDispatcher)
+            .stateIn(coroutineScope, SharingStarted.Eagerly, RingerViewModelState.Unavailable)
+
+    // Vibration attributes.
+    private val sonificiationVibrationAttributes =
+        AudioAttributes.Builder()
+            .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
+            .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION)
+            .build()
+
+    fun onRingerButtonClicked(ringerMode: RingerMode) {
+        if (drawerState.value is RingerDrawerState.Open) {
+            Events.writeEvent(Events.EVENT_RINGER_TOGGLE, ringerMode.value)
+            provideTouchFeedback(ringerMode)
+            interactor.setRingerMode(ringerMode)
+        }
+        drawerState.value =
+            when (drawerState.value) {
+                is RingerDrawerState.Initial -> {
+                    RingerDrawerState.Open(ringerMode)
+                }
+                is RingerDrawerState.Open -> {
+                    RingerDrawerState.Closed(ringerMode)
+                }
+                is RingerDrawerState.Closed -> {
+                    RingerDrawerState.Open(ringerMode)
+                }
+            }
+    }
+
+    private fun provideTouchFeedback(ringerMode: RingerMode) {
+        when (ringerMode.value) {
+            RINGER_MODE_NORMAL -> {
+                interactor.scheduleTouchFeedback()
+                null
+            }
+            RINGER_MODE_SILENT -> VibrationEffect.get(VibrationEffect.EFFECT_CLICK)
+            RINGER_MODE_VIBRATE -> VibrationEffect.get(VibrationEffect.EFFECT_DOUBLE_CLICK)
+            else -> VibrationEffect.get(VibrationEffect.EFFECT_DOUBLE_CLICK)
+        }?.let { vibrator.vibrate(it, sonificiationVibrationAttributes) }
+    }
+
+    private fun VolumeDialogRingerModel.toViewModel(
+        drawerState: RingerDrawerState
+    ): RingerViewModelState {
+        val currentIndex = availableModes.indexOf(currentRingerMode)
+        if (currentIndex == -1) {
+            volumeDialogLogger.onCurrentRingerModeIsUnsupported(currentRingerMode)
+        }
+        return if (currentIndex == -1 || isSingleVolume) {
+            RingerViewModelState.Unavailable
+        } else {
+            RingerViewModelState.Available(
+                RingerViewModel(
+                    availableButtons = availableModes.map { mode -> toButtonViewModel(mode) },
+                    currentButtonIndex = currentIndex,
+                    drawerState = drawerState,
+                )
+            )
+        }
+    }
+
+    private fun VolumeDialogRingerModel.toButtonViewModel(
+        ringerMode: RingerMode
+    ): RingerButtonViewModel? {
+        return when (ringerMode.value) {
+            RINGER_MODE_SILENT ->
+                RingerButtonViewModel(
+                    imageResId = R.drawable.ic_speaker_mute,
+                    contentDescriptionResId = R.string.volume_ringer_status_silent,
+                    hintLabelResId = R.string.volume_ringer_hint_unmute,
+                    ringerMode = ringerMode,
+                )
+            RINGER_MODE_VIBRATE ->
+                RingerButtonViewModel(
+                    imageResId = R.drawable.ic_volume_ringer_vibrate,
+                    contentDescriptionResId = R.string.volume_ringer_status_vibrate,
+                    hintLabelResId = R.string.volume_ringer_hint_vibrate,
+                    ringerMode = ringerMode,
+                )
+            RINGER_MODE_NORMAL ->
+                when {
+                    isMuted && isEnabled ->
+                        RingerButtonViewModel(
+                            imageResId = R.drawable.ic_speaker_mute,
+                            contentDescriptionResId = R.string.volume_ringer_status_normal,
+                            hintLabelResId = R.string.volume_ringer_hint_unmute,
+                            ringerMode = ringerMode,
+                        )
+
+                    availableModes.contains(RingerMode(RINGER_MODE_VIBRATE)) ->
+                        RingerButtonViewModel(
+                            imageResId = R.drawable.ic_speaker_on,
+                            contentDescriptionResId = R.string.volume_ringer_status_normal,
+                            hintLabelResId = R.string.volume_ringer_hint_vibrate,
+                            ringerMode = ringerMode,
+                        )
+
+                    else ->
+                        RingerButtonViewModel(
+                            imageResId = R.drawable.ic_speaker_on,
+                            contentDescriptionResId = R.string.volume_ringer_status_normal,
+                            hintLabelResId = R.string.volume_ringer_hint_mute,
+                            ringerMode = ringerMode,
+                        )
+                }
+            else -> null
+        }
+    }
+
+    @AssistedFactory
+    interface Factory {
+        fun create(): VolumeDialogRingerDrawerViewModel
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/settings/ui/binder/VolumeDialogSettingsButtonViewBinder.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/settings/ui/binder/VolumeDialogSettingsButtonViewBinder.kt
index b2f6cb3..2e1f82d 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/settings/ui/binder/VolumeDialogSettingsButtonViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/settings/ui/binder/VolumeDialogSettingsButtonViewBinder.kt
@@ -26,6 +26,8 @@
 import com.android.systemui.volume.dialog.settings.ui.viewmodel.VolumeDialogSettingsButtonViewModel
 import javax.inject.Inject
 import kotlinx.coroutines.awaitCancellation
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.onEach
 
 @VolumeDialogScope
 class VolumeDialogSettingsButtonViewBinder
@@ -42,7 +44,12 @@
                     factory = { viewModelFactory.create() },
                 ) { viewModel ->
                     setSnapshotBinding {
-                        visibility = if (viewModel.isVisible) View.VISIBLE else View.GONE
+                        viewModel.isVisible
+                            .onEach { isVisible ->
+                                visibility = if (isVisible) View.VISIBLE else View.GONE
+                            }
+                            .launchIn(this)
+
                         button.setOnClickListener { viewModel.onButtonClicked() }
                     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/settings/ui/viewmodel/VolumeDialogSettingsButtonViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/settings/ui/viewmodel/VolumeDialogSettingsButtonViewModel.kt
index 2acc33b..015d773 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/settings/ui/viewmodel/VolumeDialogSettingsButtonViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/settings/ui/viewmodel/VolumeDialogSettingsButtonViewModel.kt
@@ -16,9 +16,6 @@
 
 package com.android.systemui.volume.dialog.settings.ui.viewmodel
 
-import androidx.compose.runtime.getValue
-import com.android.systemui.lifecycle.ExclusiveActivatable
-import com.android.systemui.lifecycle.Hydrator
 import com.android.systemui.volume.dialog.dagger.scope.VolumeDialogScope
 import com.android.systemui.volume.dialog.settings.domain.VolumeDialogSettingsButtonInteractor
 import dagger.assisted.AssistedFactory
@@ -26,15 +23,9 @@
 
 class VolumeDialogSettingsButtonViewModel
 @AssistedInject
-constructor(private val interactor: VolumeDialogSettingsButtonInteractor) : ExclusiveActivatable() {
+constructor(private val interactor: VolumeDialogSettingsButtonInteractor) {
 
-    private val hydrator = Hydrator("VolumeDialog_settings_button")
-
-    val isVisible by hydrator.hydratedStateOf("isVisible", interactor.isVisible)
-
-    override suspend fun onActivated(): Nothing {
-        hydrator.activate()
-    }
+    val isVisible = interactor.isVisible
 
     fun onButtonClicked() {
         interactor.onButtonClicked()
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/shared/VolumeDialogLogger.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/shared/VolumeDialogLogger.kt
index 59c38c0..9a3aa7e 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/shared/VolumeDialogLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/shared/VolumeDialogLogger.kt
@@ -15,6 +15,7 @@
  */
 package com.android.systemui.volume.dialog.shared
 
+import com.android.settingslib.volume.shared.model.RingerMode
 import com.android.systemui.log.LogBuffer
 import com.android.systemui.log.core.LogLevel
 import com.android.systemui.log.dagger.VolumeLog
@@ -43,4 +44,13 @@
             { "Dismiss: ${Events.DISMISS_REASONS[int1]}" },
         )
     }
+
+    fun onCurrentRingerModeIsUnsupported(ringerMode: RingerMode) {
+        logBuffer.log(
+            TAG,
+            LogLevel.DEBUG,
+            { int1 = ringerMode.value },
+            { "Current ringer mode: $int1, ringer mode is unsupported in ringer drawer options" },
+        )
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/shared/model/VolumeDialogStreamModel.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/shared/model/VolumeDialogStreamModel.kt
index be3cd97..cec58c3 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/shared/model/VolumeDialogStreamModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/shared/model/VolumeDialogStreamModel.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.volume.dialog.shared.model
 
+import android.content.Context
 import androidx.annotation.StringRes
 import com.android.systemui.plugins.VolumeDialogController
 
@@ -29,7 +30,9 @@
     val levelMax: Int = 0,
     val muted: Boolean = false,
     val muteSupported: Boolean = false,
+    /** You likely need to use [streamLabel] instead. */
     @StringRes val name: Int = 0,
+    /** You likely need to use [streamLabel] instead. */
     val remoteLabel: String? = null,
     val routedToBluetooth: Boolean = false,
 ) {
@@ -51,3 +54,10 @@
         routedToBluetooth = legacyState.routedToBluetooth,
     )
 }
+
+fun VolumeDialogStreamModel.streamLabel(context: Context): String {
+    if (remoteLabel != null) {
+        return remoteLabel
+    }
+    return context.resources.getString(name)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSliderInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSliderInteractor.kt
index f78a8dc..876bf2c 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSliderInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSliderInteractor.kt
@@ -25,6 +25,7 @@
 import dagger.assisted.AssistedFactory
 import dagger.assisted.AssistedInject
 import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.distinctUntilChanged
 import kotlinx.coroutines.flow.mapNotNull
 
 /** Operates a state of particular slider of the Volume Dialog. */
@@ -37,12 +38,23 @@
 ) {
 
     val slider: Flow<VolumeDialogStreamModel> =
-        volumeDialogStateInteractor.volumeDialogState.mapNotNull {
-            it.streamModels[sliderType.audioStream]
-        }
+        volumeDialogStateInteractor.volumeDialogState
+            .mapNotNull {
+                it.streamModels[sliderType.audioStream]?.run {
+                    if (level < levelMin || level > levelMax) {
+                        copy(level = level.coerceIn(levelMin, levelMax))
+                    } else {
+                        this
+                    }
+                }
+            }
+            .distinctUntilChanged()
 
     fun setStreamVolume(userLevel: Int) {
-        volumeDialogController.setStreamVolume(sliderType.audioStream, userLevel)
+        with(volumeDialogController) {
+            setStreamVolume(sliderType.audioStream, userLevel)
+            setActiveStream(sliderType.audioStream)
+        }
     }
 
     @VolumeDialogScope
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderViewBinder.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderViewBinder.kt
index 25a5f28..3bf8c54 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderViewBinder.kt
@@ -16,32 +16,55 @@
 
 package com.android.systemui.volume.dialog.sliders.ui
 
+import android.animation.Animator
+import android.animation.ObjectAnimator
 import android.view.View
-import androidx.lifecycle.viewmodel.compose.viewModel
+import android.view.animation.DecelerateInterpolator
 import com.android.systemui.lifecycle.WindowLifecycleState
 import com.android.systemui.lifecycle.repeatWhenAttached
-import com.android.systemui.lifecycle.setSnapshotBinding
 import com.android.systemui.lifecycle.viewModel
+import com.android.systemui.res.R
 import com.android.systemui.volume.dialog.dagger.scope.VolumeDialogScope
+import com.android.systemui.volume.dialog.shared.model.VolumeDialogStreamModel
 import com.android.systemui.volume.dialog.sliders.ui.viewmodel.VolumeDialogSliderViewModel
+import com.android.systemui.volume.dialog.ui.utils.JankListenerFactory
+import com.android.systemui.volume.dialog.ui.utils.awaitAnimation
+import com.google.android.material.slider.LabelFormatter
+import com.google.android.material.slider.Slider
 import dagger.assisted.Assisted
 import dagger.assisted.AssistedFactory
 import dagger.assisted.AssistedInject
+import kotlin.math.roundToInt
 import kotlinx.coroutines.awaitCancellation
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.onEach
+
+private const val PROGRESS_CHANGE_ANIMATION_DURATION_MS = 80L
 
 class VolumeDialogSliderViewBinder
 @AssistedInject
-constructor(@Assisted private val viewModelProvider: () -> VolumeDialogSliderViewModel) {
+constructor(
+    @Assisted private val viewModelProvider: () -> VolumeDialogSliderViewModel,
+    private val jankListenerFactory: JankListenerFactory,
+) {
 
     fun bind(view: View) {
         with(view) {
+            val sliderView: Slider =
+                requireViewById<Slider>(R.id.volume_dialog_slider).apply {
+                    labelBehavior = LabelFormatter.LABEL_GONE
+                }
             repeatWhenAttached {
                 viewModel(
                     traceName = "VolumeDialogSliderViewBinder",
                     minWindowLifecycleState = WindowLifecycleState.ATTACHED,
                     factory = { viewModelProvider() },
                 ) { viewModel ->
-                    setSnapshotBinding {}
+                    sliderView.addOnChangeListener { _, value, fromUser ->
+                        viewModel.setStreamVolume(value.roundToInt(), fromUser)
+                    }
+
+                    viewModel.model.onEach { it.bindToSlider(sliderView) }.launchIn(this)
 
                     awaitCancellation()
                 }
@@ -49,6 +72,20 @@
         }
     }
 
+    private suspend fun VolumeDialogStreamModel.bindToSlider(slider: Slider) {
+        slider.setOnScrollChangeListener { v, scrollX, scrollY, oldScrollX, oldScrollY -> }
+        with(slider) {
+            valueFrom = levelMin.toFloat()
+            valueTo = levelMax.toFloat()
+            // coerce the current value to the new value range before animating it
+            value = value.coerceIn(valueFrom, valueTo)
+            setValueAnimated(
+                level.toFloat(),
+                jankListenerFactory.update(this, PROGRESS_CHANGE_ANIMATION_DURATION_MS),
+            )
+        }
+    }
+
     @AssistedFactory
     @VolumeDialogScope
     interface Factory {
@@ -58,3 +95,16 @@
         ): VolumeDialogSliderViewBinder
     }
 }
+
+private suspend fun Slider.setValueAnimated(
+    newValue: Float,
+    jankListener: Animator.AnimatorListener,
+) {
+    ObjectAnimator.ofFloat(value, newValue)
+        .apply {
+            duration = PROGRESS_CHANGE_ANIMATION_DURATION_MS
+            interpolator = DecelerateInterpolator()
+            addListener(jankListener)
+        }
+        .awaitAnimation<Float> { value = it }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSlidersViewBinder.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSlidersViewBinder.kt
index 0a00f70..0a4e3f4 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSlidersViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSlidersViewBinder.kt
@@ -16,15 +16,21 @@
 
 package com.android.systemui.volume.dialog.sliders.ui
 
+import android.view.LayoutInflater
 import android.view.View
+import android.view.ViewGroup
+import androidx.annotation.LayoutRes
+import androidx.compose.ui.util.fastForEachIndexed
 import com.android.systemui.lifecycle.WindowLifecycleState
 import com.android.systemui.lifecycle.repeatWhenAttached
-import com.android.systemui.lifecycle.setSnapshotBinding
 import com.android.systemui.lifecycle.viewModel
+import com.android.systemui.res.R
 import com.android.systemui.volume.dialog.dagger.scope.VolumeDialogScope
 import com.android.systemui.volume.dialog.sliders.ui.viewmodel.VolumeDialogSlidersViewModel
 import javax.inject.Inject
-import kotlinx.coroutines.awaitCancellation
+import kotlin.math.abs
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.onEach
 
 @VolumeDialogScope
 class VolumeDialogSlidersViewBinder
@@ -33,17 +39,44 @@
 
     fun bind(view: View) {
         with(view) {
+            val volumeDialog: View = requireViewById(R.id.volume_dialog)
+            val floatingSlidersContainer: ViewGroup =
+                requireViewById(R.id.volume_dialog_floating_sliders_container)
             repeatWhenAttached {
                 viewModel(
                     traceName = "VolumeDialogSlidersViewBinder",
                     minWindowLifecycleState = WindowLifecycleState.ATTACHED,
                     factory = { viewModelFactory.create() },
                 ) { viewModel ->
-                    setSnapshotBinding {}
+                    viewModel.sliders
+                        .onEach { uiModel ->
+                            uiModel.sliderViewBinder.bind(volumeDialog)
 
-                    awaitCancellation()
+                            val floatingSliderViewBinders = uiModel.floatingSliderViewBinders
+                            floatingSlidersContainer.ensureChildCount(
+                                viewLayoutId = R.layout.volume_dialog_slider_floating,
+                                count = floatingSliderViewBinders.size,
+                            )
+                            floatingSliderViewBinders.fastForEachIndexed { index, viewBinder ->
+                                viewBinder.bind(floatingSlidersContainer.getChildAt(index))
+                            }
+                        }
+                        .launchIn(this)
                 }
             }
         }
     }
 }
+
+private fun ViewGroup.ensureChildCount(@LayoutRes viewLayoutId: Int, count: Int) {
+    val childCountDelta = childCount - count
+    when {
+        childCountDelta > 0 -> {
+            removeViews(0, childCountDelta)
+        }
+        childCountDelta < 0 -> {
+            val inflater = LayoutInflater.from(context)
+            repeat(abs(childCountDelta)) { inflater.inflate(viewLayoutId, this, true) }
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderViewModel.kt
index 7ee722d..ea0b49d 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderViewModel.kt
@@ -16,26 +16,85 @@
 
 package com.android.systemui.volume.dialog.sliders.ui.viewmodel
 
+import com.android.systemui.util.time.SystemClock
+import com.android.systemui.volume.Events
+import com.android.systemui.volume.dialog.dagger.scope.VolumeDialog
+import com.android.systemui.volume.dialog.domain.interactor.VolumeDialogVisibilityInteractor
 import com.android.systemui.volume.dialog.shared.model.VolumeDialogStreamModel
 import com.android.systemui.volume.dialog.sliders.domain.interactor.VolumeDialogSliderInteractor
 import dagger.assisted.Assisted
 import dagger.assisted.AssistedFactory
 import dagger.assisted.AssistedInject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.filterNotNull
+import kotlinx.coroutines.flow.first
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.mapLatest
+import kotlinx.coroutines.flow.stateIn
 
+/*
+ This prevents volume slider updates while user interacts with it. This is needed due to the
+ flawed VolumeDialogControllerImpl. It has a single threaded message queue that handles all state
+ updates and doesn't skip sequential updates of the same stream. This leads to a bottleneck when
+ user rigorously adjusts the slider.
+
+ Remove this when getting rid of the VolumeDialogControllerImpl as this doesn't happen in the
+ Volume Panel that uses the new coroutine-backed AudioRepository.
+*/
+// TODO(b/375355785) remove this
+private const val VOLUME_UPDATE_GRACE_PERIOD = 1000
+
+@OptIn(ExperimentalCoroutinesApi::class)
 class VolumeDialogSliderViewModel
 @AssistedInject
-constructor(@Assisted private val interactor: VolumeDialogSliderInteractor) {
+constructor(
+    @Assisted private val interactor: VolumeDialogSliderInteractor,
+    private val visibilityInteractor: VolumeDialogVisibilityInteractor,
+    @VolumeDialog private val coroutineScope: CoroutineScope,
+    private val systemClock: SystemClock,
+) {
 
-    val model: Flow<VolumeDialogStreamModel> = interactor.slider
+    private val userVolumeUpdates = MutableStateFlow<VolumeUpdate?>(null)
 
-    fun setStreamVolume(volume: Int) {
-        interactor.setStreamVolume(volume)
+    val model: Flow<VolumeDialogStreamModel> =
+        interactor.slider
+            .filter {
+                val lastVolumeUpdateTime = userVolumeUpdates.value?.timestampMillis ?: 0
+                getTimestampMillis() - lastVolumeUpdateTime > VOLUME_UPDATE_GRACE_PERIOD
+            }
+            .stateIn(coroutineScope, SharingStarted.Eagerly, null)
+            .filterNotNull()
+
+    init {
+        userVolumeUpdates
+            .filterNotNull()
+            .mapLatest { volume ->
+                interactor.setStreamVolume(volume.newVolumeLevel)
+                Events.writeEvent(Events.EVENT_TOUCH_LEVEL_CHANGED, model.first().stream, volume)
+            }
+            .launchIn(coroutineScope)
     }
 
+    fun setStreamVolume(volume: Int, fromUser: Boolean) {
+        if (fromUser) {
+            visibilityInteractor.resetDismissTimeout()
+            userVolumeUpdates.value =
+                VolumeUpdate(newVolumeLevel = volume, timestampMillis = getTimestampMillis())
+        }
+    }
+
+    private fun getTimestampMillis(): Long = systemClock.uptimeMillis()
+
     @AssistedFactory
     interface Factory {
 
         fun create(interactor: VolumeDialogSliderInteractor): VolumeDialogSliderViewModel
     }
+
+    private data class VolumeUpdate(val newVolumeLevel: Int, val timestampMillis: Long)
 }
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogResourcesViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/VolumeDialogResources.kt
similarity index 95%
rename from packages/SystemUI/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogResourcesViewModel.kt
rename to packages/SystemUI/src/com/android/systemui/volume/dialog/ui/VolumeDialogResources.kt
index da9be98..e5cf62b 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogResourcesViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/VolumeDialogResources.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.volume.dialog.ui.viewmodel
+package com.android.systemui.volume.dialog.ui
 
 import android.content.Context
 import android.content.res.Resources
@@ -41,7 +41,7 @@
  * Consume or use [kotlinx.coroutines.flow.first] to get the value.
  */
 @VolumeDialogScope
-class VolumeDialogResourcesViewModel
+class VolumeDialogResources
 @Inject
 constructor(
     @VolumeDialog private val coroutineScope: CoroutineScope,
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/binder/VolumeDialogBinder.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/binder/VolumeDialogBinder.kt
deleted file mode 100644
index 77733fe..0000000
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/binder/VolumeDialogBinder.kt
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.volume.dialog.ui.binder
-
-import android.app.Dialog
-import android.graphics.Color
-import android.graphics.PixelFormat
-import android.graphics.drawable.ColorDrawable
-import android.view.ViewGroup
-import android.view.Window
-import android.view.WindowManager
-import com.android.systemui.res.R
-import com.android.systemui.volume.dialog.dagger.scope.VolumeDialog
-import com.android.systemui.volume.dialog.dagger.scope.VolumeDialogScope
-import com.android.systemui.volume.dialog.settings.ui.binder.VolumeDialogSettingsButtonViewBinder
-import com.android.systemui.volume.dialog.ui.viewmodel.VolumeDialogGravityViewModel
-import javax.inject.Inject
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.flow.launchIn
-import kotlinx.coroutines.flow.onEach
-
-/** Binds the Volume Dialog itself. */
-@VolumeDialogScope
-class VolumeDialogBinder
-@Inject
-constructor(
-    @VolumeDialog private val coroutineScope: CoroutineScope,
-    private val volumeDialogViewBinder: VolumeDialogViewBinder,
-    private val settingsButtonViewBinder: VolumeDialogSettingsButtonViewBinder,
-    private val gravityViewModel: VolumeDialogGravityViewModel,
-) {
-
-    fun bind(dialog: Dialog) {
-        with(dialog) {
-            setupWindow(window!!)
-            dialog.setContentView(R.layout.volume_dialog)
-            dialog.setCanceledOnTouchOutside(true)
-
-            settingsButtonViewBinder.bind(dialog.requireViewById(R.id.volume_dialog_settings))
-            volumeDialogViewBinder.bind(
-                dialog,
-                dialog.requireViewById(R.id.volume_dialog_container),
-            )
-        }
-    }
-
-    /** Configures [Window] for the [Dialog]. */
-    private fun setupWindow(window: Window) =
-        with(window) {
-            clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND)
-            addFlags(
-                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE or
-                    WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL or
-                    WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH or
-                    WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED
-            )
-            addPrivateFlags(WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY)
-
-            requestFeature(Window.FEATURE_NO_TITLE)
-            setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
-            setType(WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY)
-            setWindowAnimations(-1)
-            setFormat(PixelFormat.TRANSLUCENT)
-
-            attributes =
-                attributes.apply {
-                    title = "VolumeDialog" // Not the same as Window#setTitle
-                }
-            setLayout(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)
-
-            gravityViewModel.dialogGravity.onEach { window.setGravity(it) }.launchIn(coroutineScope)
-        }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/binder/VolumeDialogViewBinder.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/binder/VolumeDialogViewBinder.kt
index 23e6eac..78eabb2 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/binder/VolumeDialogViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/binder/VolumeDialogViewBinder.kt
@@ -17,19 +17,30 @@
 package com.android.systemui.volume.dialog.ui.binder
 
 import android.app.Dialog
+import android.graphics.Color
+import android.graphics.PixelFormat
+import android.graphics.drawable.ColorDrawable
 import android.view.Gravity
 import android.view.View
+import android.view.ViewGroup
+import android.view.Window
+import android.view.WindowManager
 import com.android.internal.view.RotationPolicy
 import com.android.systemui.lifecycle.WindowLifecycleState
 import com.android.systemui.lifecycle.repeatWhenAttached
 import com.android.systemui.lifecycle.viewModel
+import com.android.systemui.res.R
 import com.android.systemui.volume.SystemUIInterpolators
+import com.android.systemui.volume.dialog.dagger.scope.VolumeDialog
 import com.android.systemui.volume.dialog.dagger.scope.VolumeDialogScope
+import com.android.systemui.volume.dialog.ringer.ui.binder.VolumeDialogRingerViewBinder
+import com.android.systemui.volume.dialog.settings.ui.binder.VolumeDialogSettingsButtonViewBinder
 import com.android.systemui.volume.dialog.shared.model.VolumeDialogVisibilityModel
+import com.android.systemui.volume.dialog.sliders.ui.VolumeDialogSlidersViewBinder
+import com.android.systemui.volume.dialog.ui.VolumeDialogResources
 import com.android.systemui.volume.dialog.ui.utils.JankListenerFactory
 import com.android.systemui.volume.dialog.ui.utils.suspendAnimate
 import com.android.systemui.volume.dialog.ui.viewmodel.VolumeDialogGravityViewModel
-import com.android.systemui.volume.dialog.ui.viewmodel.VolumeDialogResourcesViewModel
 import com.android.systemui.volume.dialog.ui.viewmodel.VolumeDialogViewModel
 import com.android.systemui.volume.dialog.utils.VolumeTracer
 import javax.inject.Inject
@@ -40,6 +51,7 @@
 import kotlinx.coroutines.flow.first
 import kotlinx.coroutines.flow.launchIn
 import kotlinx.coroutines.flow.mapLatest
+import kotlinx.coroutines.flow.onEach
 
 /** Binds the root view of the Volume Dialog. */
 @OptIn(ExperimentalCoroutinesApi::class)
@@ -47,14 +59,20 @@
 class VolumeDialogViewBinder
 @Inject
 constructor(
-    private val volumeResources: VolumeDialogResourcesViewModel,
+    private val volumeResources: VolumeDialogResources,
     private val gravityViewModel: VolumeDialogGravityViewModel,
     private val viewModelFactory: VolumeDialogViewModel.Factory,
     private val jankListenerFactory: JankListenerFactory,
     private val tracer: VolumeTracer,
+    @VolumeDialog private val coroutineScope: CoroutineScope,
+    private val volumeDialogRingerViewBinder: VolumeDialogRingerViewBinder,
+    private val slidersViewBinder: VolumeDialogSlidersViewBinder,
+    private val settingsButtonViewBinder: VolumeDialogSettingsButtonViewBinder,
 ) {
 
-    fun bind(dialog: Dialog, view: View) {
+    fun bind(dialog: Dialog) {
+        setupDialog(dialog)
+        val view: View = dialog.requireViewById(R.id.volume_dialog_container)
         view.alpha = 0f
         view.repeatWhenAttached {
             view.viewModel(
@@ -62,11 +80,46 @@
                 minWindowLifecycleState = WindowLifecycleState.ATTACHED,
                 factory = { viewModelFactory.create() },
             ) { viewModel ->
+                viewModel.dialogTitle.onEach { dialog.window?.setTitle(it) }.launchIn(this)
+
                 animateVisibility(view, dialog, viewModel.dialogVisibilityModel)
 
                 awaitCancellation()
             }
         }
+        volumeDialogRingerViewBinder.bind(view)
+        slidersViewBinder.bind(view)
+        settingsButtonViewBinder.bind(view)
+    }
+
+    /** Configures [Window] for the [Dialog]. */
+    private fun setupDialog(dialog: Dialog) {
+        with(dialog.window!!) {
+            clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND)
+            addFlags(
+                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE or
+                    WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL or
+                    WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH or
+                    WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED
+            )
+            addPrivateFlags(WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY)
+
+            requestFeature(Window.FEATURE_NO_TITLE)
+            setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
+            setType(WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY)
+            setWindowAnimations(-1)
+            setFormat(PixelFormat.TRANSLUCENT)
+
+            attributes =
+                attributes.apply {
+                    title = "VolumeDialog" // Not the same as Window#setTitle
+                }
+            setLayout(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)
+
+            gravityViewModel.dialogGravity.onEach { setGravity(it) }.launchIn(coroutineScope)
+        }
+        dialog.setContentView(R.layout.volume_dialog)
+        dialog.setCanceledOnTouchOutside(true)
     }
 
     private fun CoroutineScope.animateVisibility(
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogPluginViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogPluginViewModel.kt
index f336d46..e858cfe 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogPluginViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogPluginViewModel.kt
@@ -16,8 +16,6 @@
 
 package com.android.systemui.volume.dialog.ui.viewmodel
 
-import com.android.systemui.lifecycle.ExclusiveActivatable
-import com.android.systemui.plugins.VolumeDialogController
 import com.android.systemui.volume.Events
 import com.android.systemui.volume.dialog.dagger.VolumeDialogComponent
 import com.android.systemui.volume.dialog.dagger.scope.VolumeDialogPluginScope
@@ -26,12 +24,10 @@
 import com.android.systemui.volume.dialog.shared.model.VolumeDialogVisibilityModel
 import javax.inject.Inject
 import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.awaitCancellation
 import kotlinx.coroutines.cancel
 import kotlinx.coroutines.coroutineScope
 import kotlinx.coroutines.flow.launchIn
 import kotlinx.coroutines.flow.mapLatest
-import kotlinx.coroutines.flow.onEach
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @VolumeDialogPluginScope
@@ -40,18 +36,18 @@
 constructor(
     private val componentFactory: VolumeDialogComponent.Factory,
     private val dialogVisibilityInteractor: VolumeDialogVisibilityInteractor,
-    private val controller: VolumeDialogController,
     private val logger: VolumeDialogLogger,
-) : ExclusiveActivatable() {
+) {
 
-    override suspend fun onActivated(): Nothing {
+    suspend fun launchVolumeDialog() {
         coroutineScope {
             dialogVisibilityInteractor.dialogVisibility
-                .onEach { controller.notifyVisible(it is VolumeDialogVisibilityModel.Visible) }
                 .mapLatest { visibilityModel ->
                     with(visibilityModel) {
                         if (this is VolumeDialogVisibilityModel.Visible) {
-                            showDialog(reason, keyguardLocked)
+                            showDialog(componentFactory)
+                            Events.writeEvent(Events.EVENT_SHOW_DIALOG, reason, keyguardLocked)
+                            logger.onShow(reason)
                         }
                         if (this is VolumeDialogVisibilityModel.Dismissed) {
                             Events.writeEvent(Events.EVENT_DISMISS_DIALOG, reason)
@@ -61,24 +57,18 @@
                 }
                 .launchIn(this)
         }
-        awaitCancellation()
     }
 
-    suspend fun showDialog(reason: Int, keyguardLocked: Boolean): Unit = coroutineScope {
-        logger.onShow(reason)
-
-        controller.notifyVisible(true)
-
-        val volumeDialogComponent: VolumeDialogComponent = componentFactory.create(this)
-        val dialog =
-            volumeDialogComponent.volumeDialog().apply {
-                setOnDismissListener {
-                    volumeDialogComponent.coroutineScope().cancel()
-                    dialogVisibilityInteractor.dismissDialog(Events.DISMISS_REASON_UNKNOWN)
+    private suspend fun showDialog(componentFactory: VolumeDialogComponent.Factory): Unit =
+        coroutineScope {
+            val volumeDialogComponent: VolumeDialogComponent = componentFactory.create(this)
+            val dialog =
+                volumeDialogComponent.volumeDialog().apply {
+                    setOnDismissListener {
+                        volumeDialogComponent.coroutineScope().cancel()
+                        dialogVisibilityInteractor.dismissDialog(Events.DISMISS_REASON_UNKNOWN)
+                    }
                 }
-            }
-        dialog.show()
-
-        Events.writeEvent(Events.EVENT_SHOW_DIALOG, reason, keyguardLocked)
-    }
+            dialog.show()
+        }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogViewModel.kt
index 84c837c..9be669f 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogViewModel.kt
@@ -16,25 +16,40 @@
 
 package com.android.systemui.volume.dialog.ui.viewmodel
 
-import com.android.systemui.lifecycle.ExclusiveActivatable
+import android.content.Context
+import com.android.systemui.res.R
 import com.android.systemui.volume.dialog.domain.interactor.VolumeDialogVisibilityInteractor
 import com.android.systemui.volume.dialog.shared.model.VolumeDialogVisibilityModel
+import com.android.systemui.volume.dialog.shared.model.streamLabel
+import com.android.systemui.volume.dialog.sliders.domain.interactor.VolumeDialogSliderInteractor
+import com.android.systemui.volume.dialog.sliders.domain.interactor.VolumeDialogSlidersInteractor
 import dagger.assisted.AssistedFactory
 import dagger.assisted.AssistedInject
-import kotlinx.coroutines.awaitCancellation
+import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.map
 
 /** Provides a state for the Volume Dialog. */
+@OptIn(ExperimentalCoroutinesApi::class)
 class VolumeDialogViewModel
 @AssistedInject
-constructor(dialogVisibilityInteractor: VolumeDialogVisibilityInteractor) : ExclusiveActivatable() {
+constructor(
+    private val context: Context,
+    dialogVisibilityInteractor: VolumeDialogVisibilityInteractor,
+    volumeDialogSlidersInteractor: VolumeDialogSlidersInteractor,
+    private val volumeDialogSliderInteractorFactory: VolumeDialogSliderInteractor.Factory,
+) {
 
     val dialogVisibilityModel: Flow<VolumeDialogVisibilityModel> =
         dialogVisibilityInteractor.dialogVisibility
-
-    override suspend fun onActivated(): Nothing {
-        awaitCancellation()
-    }
+    val dialogTitle: Flow<String> =
+        volumeDialogSlidersInteractor.sliders.flatMapLatest { slidersModel ->
+            val interactor = volumeDialogSliderInteractorFactory.create(slidersModel.slider)
+            interactor.slider.map { sliderModel ->
+                context.getString(R.string.volume_dialog_title, sliderModel.streamLabel(context))
+            }
+        }
 
     @AssistedFactory
     interface Factory {
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModel.kt
index 1f44451..68f7896 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModel.kt
@@ -28,6 +28,7 @@
 import com.android.settingslib.volume.shared.model.AudioStreamModel
 import com.android.settingslib.volume.shared.model.RingerMode
 import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.haptics.slider.compose.ui.SliderHapticsViewModel
 import com.android.systemui.modes.shared.ModesUiIcons
 import com.android.systemui.res.R
 import com.android.systemui.statusbar.policy.domain.interactor.ZenModeInteractor
@@ -61,6 +62,7 @@
     private val zenModeInteractor: ZenModeInteractor,
     private val uiEventLogger: UiEventLogger,
     private val volumePanelLogger: VolumePanelLogger,
+    override val hapticsViewModelFactory: SliderHapticsViewModel.Factory,
 ) : SliderViewModel {
 
     private val volumeChanges = MutableStateFlow<Int?>(null)
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/CastVolumeSliderViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/CastVolumeSliderViewModel.kt
index bb849cb..8efe915 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/CastVolumeSliderViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/CastVolumeSliderViewModel.kt
@@ -19,6 +19,7 @@
 import android.content.Context
 import android.media.session.MediaController.PlaybackInfo
 import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.haptics.slider.compose.ui.SliderHapticsViewModel
 import com.android.systemui.res.R
 import com.android.systemui.volume.panel.component.mediaoutput.domain.interactor.MediaDeviceSessionInteractor
 import com.android.systemui.volume.panel.component.mediaoutput.shared.model.MediaDeviceSession
@@ -40,6 +41,7 @@
     @Assisted private val coroutineScope: CoroutineScope,
     private val context: Context,
     private val mediaDeviceSessionInteractor: MediaDeviceSessionInteractor,
+    override val hapticsViewModelFactory: SliderHapticsViewModel.Factory,
 ) : SliderViewModel {
 
     override val slider: StateFlow<SliderState> =
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/SliderViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/SliderViewModel.kt
index 7ded8c5..9c1783b 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/SliderViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/SliderViewModel.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.volume.panel.component.volume.slider.ui.viewmodel
 
+import com.android.systemui.haptics.slider.compose.ui.SliderHapticsViewModel
 import kotlinx.coroutines.flow.StateFlow
 
 /** Controls the behaviour of a volume slider. */
@@ -23,6 +24,8 @@
 
     val slider: StateFlow<SliderState>
 
+    val hapticsViewModelFactory: SliderHapticsViewModel.Factory
+
     fun onValueChanged(state: SliderState, newValue: Float)
 
     fun onValueChangeFinished()
diff --git a/packages/SystemUI/src/com/android/systemui/wallpapers/data/repository/NoopWallpaperRepository.kt b/packages/SystemUI/src/com/android/systemui/wallpapers/data/repository/NoopWallpaperRepository.kt
index 54953c9..9055d18 100644
--- a/packages/SystemUI/src/com/android/systemui/wallpapers/data/repository/NoopWallpaperRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/wallpapers/data/repository/NoopWallpaperRepository.kt
@@ -35,6 +35,4 @@
     override val wallpaperInfo: StateFlow<WallpaperInfo?> = MutableStateFlow(null).asStateFlow()
     override val wallpaperSupportsAmbientMode = MutableStateFlow(false).asStateFlow()
     override var rootView: View? = null
-
-    override fun setNotificationStackAbsoluteBottom(bottom: Float) {}
 }
diff --git a/packages/SystemUI/src/com/android/systemui/wallpapers/data/repository/WallpaperRepository.kt b/packages/SystemUI/src/com/android/systemui/wallpapers/data/repository/WallpaperRepository.kt
index 0f6e960..015b480 100644
--- a/packages/SystemUI/src/com/android/systemui/wallpapers/data/repository/WallpaperRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/wallpapers/data/repository/WallpaperRepository.kt
@@ -45,7 +45,6 @@
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.asStateFlow
-import kotlinx.coroutines.flow.collect
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.filter
 import kotlinx.coroutines.flow.map
@@ -64,12 +63,6 @@
 
     /** Set rootView to get its windowToken afterwards */
     var rootView: View?
-
-    /**
-     * Set bottom of notifications from notification stack, and Magic Portrait will layout base on
-     * this value
-     */
-    fun setNotificationStackAbsoluteBottom(bottom: Float)
 }
 
 @SysUISingleton
@@ -106,7 +99,8 @@
             .filter { it.selectionStatus == SelectionStatus.SELECTION_COMPLETE }
 
     /** The bottom of notification stack respect to the top of screen. */
-    private val notificationStackAbsoluteBottom: MutableStateFlow<Float> = MutableStateFlow(0F)
+    private val notificationStackAbsoluteBottom: StateFlow<Float> =
+        keyguardRepository.notificationStackAbsoluteBottom
 
     /** The top of shortcut respect to the top of screen. */
     private val shortcutAbsoluteTop: StateFlow<Float> = keyguardRepository.shortcutAbsoluteTop
@@ -206,10 +200,6 @@
                 initialValue = false,
             )
 
-    override fun setNotificationStackAbsoluteBottom(bottom: Float) {
-        notificationStackAbsoluteBottom.value = bottom
-    }
-
     private suspend fun getWallpaper(selectedUser: SelectedUserModel): WallpaperInfo? {
         return withContext(bgDispatcher) {
             wallpaperManager.getWallpaperInfoForUser(selectedUser.userInfo.id)
diff --git a/packages/SystemUI/src/com/android/systemui/wallpapers/domain/interactor/WallpaperInteractor.kt b/packages/SystemUI/src/com/android/systemui/wallpapers/domain/interactor/WallpaperInteractor.kt
index fe6977c..88795ca 100644
--- a/packages/SystemUI/src/com/android/systemui/wallpapers/domain/interactor/WallpaperInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/wallpapers/domain/interactor/WallpaperInteractor.kt
@@ -21,10 +21,6 @@
 import kotlinx.coroutines.flow.StateFlow
 
 class WallpaperInteractor @Inject constructor(val wallpaperRepository: WallpaperRepository) {
-    fun setNotificationStackAbsoluteBottom(bottom: Float) {
-        wallpaperRepository.setNotificationStackAbsoluteBottom(bottom)
-    }
-
     val wallpaperSupportsAmbientMode: StateFlow<Boolean> =
         wallpaperRepository.wallpaperSupportsAmbientMode
 }
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index 34ebba8..3d9eb53 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -159,6 +159,7 @@
 import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
 import com.android.systemui.util.kotlin.JavaAdapter;
 import com.android.systemui.util.settings.GlobalSettings;
+import com.android.systemui.utils.FieldSetter;
 
 import org.junit.After;
 import org.junit.Assert;
@@ -172,7 +173,6 @@
 import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
 import org.mockito.MockitoSession;
-import org.mockito.internal.util.reflection.FieldSetter;
 import org.mockito.quality.Strictness;
 
 import java.util.ArrayList;
@@ -2267,6 +2267,23 @@
     }
 
     @Test
+    public void isSimPinSecureReturnsFalseWhenEmpty() {
+        assertThat(mKeyguardUpdateMonitor.isSimPinSecure()).isFalse();
+    }
+
+    @Test
+    public void isSimPinSecureReturnsTrueWhenOneSlotIsLocked() {
+        // Slot 0 is locked
+        mKeyguardUpdateMonitor.handleSimStateChange(10, 0,
+                TelephonyManager.SIM_STATE_PIN_REQUIRED);
+        // Slot 1 is not ready
+        mKeyguardUpdateMonitor.handleSimStateChange(11, 1,
+                TelephonyManager.SIM_STATE_NOT_READY);
+
+        assertThat(mKeyguardUpdateMonitor.isSimPinSecure()).isTrue();
+    }
+
+    @Test
     public void onAuthEnrollmentChangesCallbacksAreNotified() {
         KeyguardUpdateMonitorCallback callback = mock(KeyguardUpdateMonitorCallback.class);
         ArgumentCaptor<AuthController.Callback> authCallback = ArgumentCaptor.forClass(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
index 344d065..0769ada 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
@@ -104,6 +104,7 @@
 import com.android.systemui.settings.UserTracker;
 import com.android.systemui.statusbar.commandline.CommandRegistry;
 import com.android.systemui.statusbar.events.PrivacyDotViewController;
+import com.android.systemui.util.concurrency.DelayableExecutor;
 import com.android.systemui.util.concurrency.FakeExecutor;
 import com.android.systemui.util.concurrency.FakeThreadFactory;
 import com.android.systemui.util.kotlin.JavaAdapter;
@@ -186,16 +187,17 @@
     private List<DecorProvider> mMockCutoutList;
     private final CameraProtectionLoader mCameraProtectionLoader =
             new CameraProtectionLoaderImpl(mContext);
+    private Handler mMainHandler;
 
     @Before
     public void setup() {
         MockitoAnnotations.initMocks(this);
 
-        Handler mainHandler = new Handler(TestableLooper.get(this).getLooper());
+        mMainHandler = new Handler(TestableLooper.get(this).getLooper());
         mSecureSettings = new FakeSettings();
         mExecutor = new FakeExecutor(new FakeSystemClock());
         mThreadFactory = new FakeThreadFactory(mExecutor);
-        mThreadFactory.setHandler(mainHandler);
+        mThreadFactory.setHandler(mMainHandler);
 
         mWindowManager = mock(WindowManager.class);
         WindowMetrics metrics = mContext.getSystemService(WindowManager.class)
@@ -214,26 +216,26 @@
         when(mMockTypedArray.length()).thenReturn(0);
         mPrivacyDotTopLeftDecorProvider = spy(new PrivacyDotCornerDecorProviderImpl(
                 R.id.privacy_dot_top_left_container,
-                DisplayCutout.BOUNDS_POSITION_TOP,
-                DisplayCutout.BOUNDS_POSITION_LEFT,
+                BOUNDS_POSITION_TOP,
+                BOUNDS_POSITION_LEFT,
                 R.layout.privacy_dot_top_left));
 
         mPrivacyDotTopRightDecorProvider = spy(new PrivacyDotCornerDecorProviderImpl(
                 R.id.privacy_dot_top_right_container,
-                DisplayCutout.BOUNDS_POSITION_TOP,
-                DisplayCutout.BOUNDS_POSITION_RIGHT,
+                BOUNDS_POSITION_TOP,
+                BOUNDS_POSITION_RIGHT,
                 R.layout.privacy_dot_top_right));
 
         mPrivacyDotBottomLeftDecorProvider = spy(new PrivacyDotCornerDecorProviderImpl(
                 R.id.privacy_dot_bottom_left_container,
-                DisplayCutout.BOUNDS_POSITION_BOTTOM,
-                DisplayCutout.BOUNDS_POSITION_LEFT,
+                BOUNDS_POSITION_BOTTOM,
+                BOUNDS_POSITION_LEFT,
                 R.layout.privacy_dot_bottom_left));
 
         mPrivacyDotBottomRightDecorProvider = spy(new PrivacyDotCornerDecorProviderImpl(
                 R.id.privacy_dot_bottom_right_container,
-                DisplayCutout.BOUNDS_POSITION_BOTTOM,
-                DisplayCutout.BOUNDS_POSITION_RIGHT,
+                BOUNDS_POSITION_BOTTOM,
+                BOUNDS_POSITION_RIGHT,
                 R.layout.privacy_dot_bottom_right));
 
         // Default no cutout
@@ -256,11 +258,10 @@
                 mLazyViewCapture, false);
         mScreenDecorations = spy(new ScreenDecorations(mContext, mSecureSettings,
                 mCommandRegistry, mUserTracker, mDisplayTracker, mDotViewController,
-                mThreadFactory,
                 mPrivacyDotDecorProviderFactory, mFaceScanningProviderFactory,
                 new ScreenDecorationsLogger(logcatLogBuffer("TestLogBuffer")),
                 mFakeFacePropertyRepository, mJavaAdapter, mCameraProtectionLoader,
-                mViewCaptureAwareWindowManager) {
+                mViewCaptureAwareWindowManager, mMainHandler, mExecutor) {
             @Override
             public void start() {
                 super.start();
@@ -1272,10 +1273,10 @@
         ScreenDecorations screenDecorations = new ScreenDecorations(mContext,
                 mSecureSettings, mCommandRegistry, mUserTracker, mDisplayTracker,
                 mDotViewController,
-                mThreadFactory, mPrivacyDotDecorProviderFactory, mFaceScanningProviderFactory,
+                mPrivacyDotDecorProviderFactory, mFaceScanningProviderFactory,
                 new ScreenDecorationsLogger(logcatLogBuffer("TestLogBuffer")),
                 mFakeFacePropertyRepository, mJavaAdapter, mCameraProtectionLoader,
-                mViewCaptureAwareWindowManager);
+                mViewCaptureAwareWindowManager, mMainHandler, mExecutor);
         screenDecorations.start();
         when(mContext.getDisplay()).thenReturn(mDisplay);
         when(mDisplay.getDisplayInfo(any())).thenAnswer(new Answer<Boolean>() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/data/repository/AccessibilityQsShortcutsRepositoryImplForDeviceTest.kt b/packages/SystemUI/tests/src/com/android/systemui/accessibility/data/repository/AccessibilityQsShortcutsRepositoryImplForDeviceTest.kt
index 1d1329ac..4e0ebae 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/data/repository/AccessibilityQsShortcutsRepositoryImplForDeviceTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/data/repository/AccessibilityQsShortcutsRepositoryImplForDeviceTest.kt
@@ -34,6 +34,7 @@
 import com.android.systemui.qs.tiles.ReduceBrightColorsTile
 import com.android.systemui.util.mockito.whenever
 import com.android.systemui.util.settings.FakeSettings
+import com.android.systemui.utils.FieldSetter
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.test.StandardTestDispatcher
@@ -46,7 +47,6 @@
 import org.junit.runner.RunWith
 import org.mockito.Mock
 import org.mockito.Mockito
-import org.mockito.internal.util.reflection.FieldSetter
 import org.mockito.junit.MockitoJUnit
 import org.mockito.junit.MockitoRule
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/composable/BouncerContentTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/composable/BouncerContentTest.kt
index 6e36d42b..9ace8e9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/composable/BouncerContentTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/composable/BouncerContentTest.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.bouncer.ui.composable
 
 import android.app.AlertDialog
+import android.content.testableContext
 import android.platform.test.annotations.MotionTest
 import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.runtime.Composable
@@ -38,8 +39,10 @@
 import com.android.systemui.flags.fakeFeatureFlagsClassic
 import com.android.systemui.lifecycle.rememberViewModel
 import com.android.systemui.motion.createSysUiComposeMotionTestRule
+import com.android.systemui.res.R
 import com.android.systemui.scene.domain.startable.sceneContainerStartable
 import com.android.systemui.testKosmos
+import org.junit.After
 import org.junit.Before
 import org.junit.Rule
 import org.junit.Test
@@ -76,6 +79,17 @@
         kosmos.sceneContainerStartable.start()
         kosmos.fakeFeatureFlagsClassic.set(Flags.FULL_SCREEN_USER_SWITCHER, true)
         kosmos.fakeAuthenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
+        kosmos.testableContext.orCreateTestableResources.addOverride(
+            R.bool.config_enableBouncerUserSwitcher,
+            true,
+        )
+    }
+
+    @After
+    fun teardown() {
+        kosmos.testableContext.orCreateTestableResources.removeOverride(
+            R.bool.config_enableBouncerUserSwitcher
+        )
     }
 
     @Composable
diff --git a/packages/SystemUI/tests/src/com/android/systemui/display/data/repository/DisplayScopeRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/display/data/repository/DisplayScopeRepositoryImplTest.kt
index 5d5c120..da7a723 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/display/data/repository/DisplayScopeRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/display/data/repository/DisplayScopeRepositoryImplTest.kt
@@ -23,7 +23,7 @@
 import com.android.systemui.kosmos.applicationCoroutineScope
 import com.android.systemui.kosmos.testDispatcher
 import com.android.systemui.kosmos.testScope
-import com.android.systemui.kosmos.unconfinedTestDispatcher
+import com.android.systemui.kosmos.useUnconfinedTestDispatcher
 import com.android.systemui.statusbar.core.StatusBarConnectedDisplays
 import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
@@ -38,7 +38,7 @@
 @SmallTest
 class DisplayScopeRepositoryImplTest : SysuiTestCase() {
 
-    private val kosmos = testKosmos().also { it.testDispatcher = it.unconfinedTestDispatcher }
+    private val kosmos = testKosmos().useUnconfinedTestDispatcher()
     private val testScope = kosmos.testScope
     private val fakeDisplayRepository = kosmos.displayRepository
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt
index fa69fdd..929b0aa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt
@@ -50,8 +50,9 @@
 import com.android.systemui.keyguard.ui.preview.KeyguardPreviewRenderer
 import com.android.systemui.keyguard.ui.preview.KeyguardPreviewRendererFactory
 import com.android.systemui.keyguard.ui.preview.KeyguardRemotePreviewManager
-import com.android.systemui.kosmos.unconfinedTestDispatcher
-import com.android.systemui.kosmos.unconfinedTestScope
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.kosmos.useUnconfinedTestDispatcher
 import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.res.R
 import com.android.systemui.scene.domain.interactor.sceneInteractor
@@ -67,7 +68,6 @@
 import com.android.systemui.util.mockito.mock
 import com.android.systemui.util.mockito.whenever
 import com.android.systemui.util.settings.fakeSettings
-import com.android.systemui.util.settings.unconfinedDispatcherFakeSettings
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.MutableStateFlow
@@ -89,10 +89,10 @@
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
 class CustomizationProviderTest : SysuiTestCase() {
 
-    private val kosmos = testKosmos()
-    private val testDispatcher = kosmos.unconfinedTestDispatcher
-    private val testScope = kosmos.unconfinedTestScope
-    private val fakeSettings = kosmos.unconfinedDispatcherFakeSettings
+    private val kosmos = testKosmos().useUnconfinedTestDispatcher()
+    private val testDispatcher = kosmos.testDispatcher
+    private val testScope = kosmos.testScope
+    private val fakeSettings = kosmos.fakeSettings
 
     @Mock private lateinit var lockPatternUtils: LockPatternUtils
     @Mock private lateinit var keyguardStateController: KeyguardStateController
@@ -129,13 +129,7 @@
                 context = context,
                 userFileManager =
                     mock<UserFileManager>().apply {
-                        whenever(
-                                getSharedPreferences(
-                                    anyString(),
-                                    anyInt(),
-                                    anyInt(),
-                                )
-                            )
+                        whenever(getSharedPreferences(anyString(), anyInt(), anyInt()))
                             .thenReturn(FakeSharedPreferences())
                     },
                 userTracker = userTracker,
@@ -288,10 +282,7 @@
             val affordanceId = AFFORDANCE_2
             val affordanceName = AFFORDANCE_2_NAME
 
-            insertSelection(
-                slotId = slotId,
-                affordanceId = affordanceId,
-            )
+            insertSelection(slotId = slotId, affordanceId = affordanceId)
 
             assertThat(querySelections())
                 .isEqualTo(
@@ -311,14 +302,8 @@
             assertThat(querySlots())
                 .isEqualTo(
                     listOf(
-                        Slot(
-                            id = KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START,
-                            capacity = 1,
-                        ),
-                        Slot(
-                            id = KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_END,
-                            capacity = 1,
-                        ),
+                        Slot(id = KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START, capacity = 1),
+                        Slot(id = KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_END, capacity = 1),
                     )
                 )
             runCurrent()
@@ -330,16 +315,8 @@
             assertThat(queryAffordances())
                 .isEqualTo(
                     listOf(
-                        Affordance(
-                            id = AFFORDANCE_1,
-                            name = AFFORDANCE_1_NAME,
-                            iconResourceId = 1,
-                        ),
-                        Affordance(
-                            id = AFFORDANCE_2,
-                            name = AFFORDANCE_2_NAME,
-                            iconResourceId = 2,
-                        ),
+                        Affordance(id = AFFORDANCE_1, name = AFFORDANCE_1_NAME, iconResourceId = 1),
+                        Affordance(id = AFFORDANCE_2, name = AFFORDANCE_2_NAME, iconResourceId = 2),
                     )
                 )
         }
@@ -361,10 +338,7 @@
                 "${Contract.LockScreenQuickAffordances.SelectionTable.Columns.SLOT_ID} = ? AND" +
                     " ${Contract.LockScreenQuickAffordances.SelectionTable.Columns.AFFORDANCE_ID}" +
                     " = ?",
-                arrayOf(
-                    KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_END,
-                    AFFORDANCE_2,
-                ),
+                arrayOf(KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_END, AFFORDANCE_2),
             )
 
             assertThat(querySelections())
@@ -394,9 +368,7 @@
             context.contentResolver.delete(
                 Contract.LockScreenQuickAffordances.SelectionTable.URI,
                 Contract.LockScreenQuickAffordances.SelectionTable.Columns.SLOT_ID,
-                arrayOf(
-                    KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_END,
-                ),
+                arrayOf(KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_END),
             )
 
             assertThat(querySelections())
@@ -428,31 +400,22 @@
             assertThat(result.containsKey(KeyguardRemotePreviewManager.KEY_PREVIEW_CALLBACK))
         }
 
-    private fun insertSelection(
-        slotId: String,
-        affordanceId: String,
-    ) {
+    private fun insertSelection(slotId: String, affordanceId: String) {
         context.contentResolver.insert(
             Contract.LockScreenQuickAffordances.SelectionTable.URI,
             ContentValues().apply {
                 put(Contract.LockScreenQuickAffordances.SelectionTable.Columns.SLOT_ID, slotId)
                 put(
                     Contract.LockScreenQuickAffordances.SelectionTable.Columns.AFFORDANCE_ID,
-                    affordanceId
+                    affordanceId,
                 )
-            }
+            },
         )
     }
 
     private fun querySelections(): List<Selection> {
         return context.contentResolver
-            .query(
-                Contract.LockScreenQuickAffordances.SelectionTable.URI,
-                null,
-                null,
-                null,
-                null,
-            )
+            .query(Contract.LockScreenQuickAffordances.SelectionTable.URI, null, null, null, null)
             ?.use { cursor ->
                 buildList {
                     val slotIdColumnIndex =
@@ -491,13 +454,7 @@
 
     private fun querySlots(): List<Slot> {
         return context.contentResolver
-            .query(
-                Contract.LockScreenQuickAffordances.SlotTable.URI,
-                null,
-                null,
-                null,
-                null,
-            )
+            .query(Contract.LockScreenQuickAffordances.SlotTable.URI, null, null, null, null)
             ?.use { cursor ->
                 buildList {
                     val idColumnIndex =
@@ -526,13 +483,7 @@
 
     private fun queryAffordances(): List<Affordance> {
         return context.contentResolver
-            .query(
-                Contract.LockScreenQuickAffordances.AffordanceTable.URI,
-                null,
-                null,
-                null,
-                null,
-            )
+            .query(Contract.LockScreenQuickAffordances.AffordanceTable.URI, null, null, null, null)
             ?.use { cursor ->
                 buildList {
                     val idColumnIndex =
@@ -564,22 +515,11 @@
             } ?: emptyList()
     }
 
-    data class Slot(
-        val id: String,
-        val capacity: Int,
-    )
+    data class Slot(val id: String, val capacity: Int)
 
-    data class Affordance(
-        val id: String,
-        val name: String,
-        val iconResourceId: Int,
-    )
+    data class Affordance(val id: String, val name: String, val iconResourceId: Int)
 
-    data class Selection(
-        val slotId: String,
-        val affordanceId: String,
-        val affordanceName: String,
-    )
+    data class Selection(val slotId: String, val affordanceId: String, val affordanceName: String)
 
     companion object {
         private const val AFFORDANCE_1 = "affordance_1"
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 570c640..33b61a09 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
@@ -48,7 +48,7 @@
 import com.android.systemui.kosmos.applicationCoroutineScope
 import com.android.systemui.kosmos.testDispatcher
 import com.android.systemui.kosmos.testScope
-import com.android.systemui.kosmos.unconfinedTestDispatcher
+import com.android.systemui.kosmos.useUnconfinedTestDispatcher
 import com.android.systemui.media.controls.MediaTestUtils
 import com.android.systemui.media.controls.domain.pipeline.EMPTY_SMARTSPACE_MEDIA_DATA
 import com.android.systemui.media.controls.domain.pipeline.MediaDataManager
@@ -73,7 +73,7 @@
 import com.android.systemui.testKosmos
 import com.android.systemui.util.concurrency.FakeExecutor
 import com.android.systemui.util.settings.GlobalSettings
-import com.android.systemui.util.settings.unconfinedDispatcherFakeSettings
+import com.android.systemui.util.settings.fakeSettings
 import com.android.systemui.util.time.FakeSystemClock
 import com.google.common.truth.Truth.assertThat
 import java.util.Locale
@@ -119,9 +119,9 @@
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
 @RunWith(ParameterizedAndroidJunit4::class)
 class MediaCarouselControllerTest(flags: FlagsParameterization) : SysuiTestCase() {
-    private val kosmos = testKosmos()
-    private val testDispatcher = kosmos.unconfinedTestDispatcher
-    private val secureSettings = kosmos.unconfinedDispatcherFakeSettings
+    private val kosmos = testKosmos().useUnconfinedTestDispatcher()
+    private val testDispatcher = kosmos.testDispatcher
+    private val secureSettings = kosmos.fakeSettings
 
     @Mock lateinit var mediaControlPanelFactory: Provider<MediaControlPanel>
     @Mock lateinit var mediaViewControllerFactory: Provider<MediaViewController>
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.kt
index 8aaa121..3c922dc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.kt
@@ -13,6 +13,7 @@
  */
 package com.android.systemui.qs
 
+import android.content.res.Configuration
 import android.graphics.Rect
 import android.platform.test.flag.junit.FlagsParameterization
 import android.testing.TestableContext
@@ -91,7 +92,9 @@
 
     @After
     fun tearDown() {
-        ViewUtils.detachView(qsPanel)
+        if (qsPanel.isAttachedToWindow) {
+            ViewUtils.detachView(qsPanel)
+        }
     }
 
     @Test
@@ -119,7 +122,7 @@
         qsPanel.tileLayout?.addTile(
             QSPanelControllerBase.TileRecord(
                 mock(QSTile::class.java),
-                QSTileViewImpl(themedContext)
+                QSTileViewImpl(themedContext),
             )
         )
 
@@ -129,7 +132,7 @@
         qsPanel.setUsingHorizontalLayout(/* horizontal */ true, mediaView, /* force */ true)
         qsPanel.measure(
             /* width */ View.MeasureSpec.makeMeasureSpec(3000, View.MeasureSpec.EXACTLY),
-            /* height */ View.MeasureSpec.makeMeasureSpec(1000, View.MeasureSpec.EXACTLY)
+            /* height */ View.MeasureSpec.makeMeasureSpec(1000, View.MeasureSpec.EXACTLY),
         )
         qsPanel.layout(0, 0, qsPanel.measuredWidth, qsPanel.measuredHeight)
 
@@ -147,7 +150,7 @@
         val padding = 10
         themedContext.orCreateTestableResources.addOverride(
             R.dimen.qs_panel_padding_bottom,
-            padding
+            padding,
         )
         qsPanel.updatePadding()
         assertThat(qsPanel.paddingBottom).isEqualTo(padding)
@@ -160,7 +163,7 @@
         themedContext.orCreateTestableResources.addOverride(R.dimen.qs_panel_padding_top, padding)
         themedContext.orCreateTestableResources.addOverride(
             R.dimen.qs_panel_padding_top,
-            paddingCombined
+            paddingCombined,
         )
 
         qsPanel.updatePadding()
@@ -267,6 +270,62 @@
         assertThat(qsPanel.tileLayout!!.maxColumns).isEqualTo(2)
     }
 
+    @Test
+    fun noPendingConfigChangesAtBeginning() {
+        assertThat(qsPanel.hadConfigurationChangeWhileDetached()).isFalse()
+    }
+
+    @Test
+    fun configChangesWhileDetached_pendingConfigChanges() {
+        ViewUtils.detachView(qsPanel)
+
+        qsPanel.onConfigurationChanged(Configuration())
+
+        assertThat(qsPanel.hadConfigurationChangeWhileDetached()).isTrue()
+    }
+
+    @Test
+    fun configChangesWhileDetached_reattach_pendingConfigChanges() {
+        ViewUtils.detachView(qsPanel)
+
+        qsPanel.onConfigurationChanged(Configuration())
+        testableLooper.runWithLooper { ViewUtils.attachView(qsPanel) }
+
+        assertThat(qsPanel.hadConfigurationChangeWhileDetached()).isTrue()
+    }
+
+    @Test
+    fun configChangesWhileDetached_reattach_detach_pendingConfigChanges_reset() {
+        ViewUtils.detachView(qsPanel)
+
+        qsPanel.onConfigurationChanged(Configuration())
+
+        testableLooper.runWithLooper { ViewUtils.attachView(qsPanel) }
+        ViewUtils.detachView(qsPanel)
+
+        assertThat(qsPanel.hadConfigurationChangeWhileDetached()).isFalse()
+    }
+
+    @Test
+    fun configChangeWhileAttached_noPendingConfigChanges() {
+        qsPanel.onConfigurationChanged(Configuration())
+
+        assertThat(qsPanel.hadConfigurationChangeWhileDetached()).isFalse()
+    }
+
+    @Test
+    fun configChangeWhileAttachedWithPending_doesntResetPending() {
+        ViewUtils.detachView(qsPanel)
+
+        qsPanel.onConfigurationChanged(Configuration())
+
+        testableLooper.runWithLooper { ViewUtils.attachView(qsPanel) }
+
+        qsPanel.onConfigurationChanged(Configuration())
+
+        assertThat(qsPanel.hadConfigurationChangeWhileDetached()).isTrue()
+    }
+
     companion object {
         @Parameters(name = "{0}") @JvmStatic fun getParams() = parameterizeSceneContainerFlag()
     }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/settings/brightness/BrightnessDialogTest.kt b/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessDialogTest.kt
similarity index 81%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/settings/brightness/BrightnessDialogTest.kt
rename to packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessDialogTest.kt
index a06784e..f7059e2 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/settings/brightness/BrightnessDialogTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessDialogTest.kt
@@ -18,25 +18,30 @@
 
 import android.content.Intent
 import android.graphics.Rect
+import android.platform.test.flag.junit.FlagsParameterization
 import android.testing.TestableLooper
 import android.view.View
 import android.view.ViewGroup
 import android.view.WindowManagerPolicyConstants.EXTRA_FROM_BRIGHTNESS_KEY
-import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.FlakyTest
 import androidx.test.filters.SmallTest
 import androidx.test.rule.ActivityTestRule
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.activity.SingleActivityFactory
+import com.android.systemui.brightness.ui.viewmodel.BrightnessSliderViewModel
+import com.android.systemui.brightness.ui.viewmodel.brightnessSliderViewModelFactory
+import com.android.systemui.qs.flags.QSComposeFragment
 import com.android.systemui.res.R
 import com.android.systemui.shade.domain.interactor.ShadeInteractor
 import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper
+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.whenever
 import com.android.systemui.util.time.FakeSystemClock
 import com.google.common.truth.Truth.assertThat
+import java.util.concurrent.CountDownLatch
 import kotlin.time.Duration.Companion.milliseconds
 import kotlinx.coroutines.FlowPreview
 import kotlinx.coroutines.flow.MutableStateFlow
@@ -53,11 +58,25 @@
 import org.mockito.Mockito.eq
 import org.mockito.Mockito.`when`
 import org.mockito.MockitoAnnotations
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
 
 @SmallTest
-@RunWith(AndroidJUnit4::class)
+@RunWith(ParameterizedAndroidJunit4::class)
 @TestableLooper.RunWithLooper
-class BrightnessDialogTest : SysuiTestCase() {
+class BrightnessDialogTest(val flags: FlagsParameterization) : SysuiTestCase() {
+
+    init {
+        mSetFlagsRule.setFlagsParameterization(flags)
+    }
+
+    val viewId by lazy {
+        if (QSComposeFragment.isEnabled) {
+            R.id.brightness_dialog_slider
+        } else {
+            R.id.brightness_mirror_container
+        }
+    }
 
     @Mock private lateinit var brightnessSliderControllerFactory: BrightnessSliderController.Factory
     @Mock private lateinit var brightnessSliderController: BrightnessSliderController
@@ -66,10 +85,14 @@
     @Mock private lateinit var accessibilityMgr: AccessibilityManagerWrapper
     @Mock private lateinit var shadeInteractor: ShadeInteractor
 
+    private val kosmos = testKosmos()
+
     private val clock = FakeSystemClock()
     private val mainExecutor = FakeExecutor(clock)
 
-    @Rule
+    private val onDestroyLatch = CountDownLatch(1)
+
+    @Rule(order = 200)
     @JvmField
     var activityRule =
         ActivityTestRule(
@@ -79,7 +102,9 @@
                     brightnessControllerFactory,
                     mainExecutor,
                     accessibilityMgr,
-                    shadeInteractor
+                    shadeInteractor,
+                    kosmos.brightnessSliderViewModelFactory,
+                    onDestroyLatch,
                 )
             },
             /* initialTouchMode= */ false,
@@ -99,12 +124,13 @@
     @After
     fun tearDown() {
         activityRule.finishActivity()
+        onDestroyLatch.await()
     }
 
     @Test
     fun testGestureExclusion() {
         activityRule.launchActivity(Intent(Intent.ACTION_SHOW_BRIGHTNESS_DIALOG))
-        val frame = activityRule.activity.requireViewById<View>(R.id.brightness_mirror_container)
+        val frame = activityRule.activity.requireViewById<View>(viewId)
 
         val lp = frame.layoutParams as ViewGroup.MarginLayoutParams
         val horizontalMargin =
@@ -125,7 +151,7 @@
         `when`(
                 accessibilityMgr.getRecommendedTimeoutMillis(
                     eq(BrightnessDialog.DIALOG_TIMEOUT_MILLIS),
-                    anyInt()
+                    anyInt(),
                 )
             )
             .thenReturn(BrightnessDialog.DIALOG_TIMEOUT_MILLIS)
@@ -144,7 +170,7 @@
         `when`(
                 accessibilityMgr.getRecommendedTimeoutMillis(
                     eq(BrightnessDialog.DIALOG_TIMEOUT_MILLIS),
-                    anyInt()
+                    anyInt(),
                 )
             )
             .thenReturn(BrightnessDialog.DIALOG_TIMEOUT_MILLIS)
@@ -171,7 +197,7 @@
         `when`(
                 accessibilityMgr.getRecommendedTimeoutMillis(
                     eq(BrightnessDialog.DIALOG_TIMEOUT_MILLIS),
-                    anyInt()
+                    anyInt(),
                 )
             )
             .thenReturn(BrightnessDialog.DIALOG_TIMEOUT_MILLIS)
@@ -205,14 +231,17 @@
         brightnessControllerFactory: BrightnessController.Factory,
         mainExecutor: DelayableExecutor,
         accessibilityMgr: AccessibilityManagerWrapper,
-        shadeInteractor: ShadeInteractor
+        shadeInteractor: ShadeInteractor,
+        brightnessSliderViewModelFactory: BrightnessSliderViewModel.Factory,
+        private val countdownLatch: CountDownLatch,
     ) :
         BrightnessDialog(
             brightnessSliderControllerFactory,
             brightnessControllerFactory,
             mainExecutor,
             accessibilityMgr,
-            shadeInteractor
+            shadeInteractor,
+            brightnessSliderViewModelFactory,
         ) {
         var finishing = MutableStateFlow(false)
 
@@ -223,5 +252,18 @@
         override fun requestFinish() {
             finishing.value = true
         }
+
+        override fun onDestroy() {
+            super.onDestroy()
+            countdownLatch.countDown()
+        }
+    }
+
+    companion object {
+        @JvmStatic
+        @Parameters(name = "{0}")
+        fun getParams(): List<FlagsParameterization> {
+            return FlagsParameterization.allCombinationsOf(QSComposeFragment.FLAG_NAME)
+        }
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java
index 2cf599a..e21a005 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java
@@ -45,6 +45,7 @@
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.inOrder;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
@@ -63,6 +64,7 @@
 import android.app.NotificationManager;
 import android.os.Handler;
 import android.os.RemoteException;
+import android.platform.test.annotations.EnableFlags;
 import android.service.notification.NotificationListenerService.Ranking;
 import android.service.notification.NotificationListenerService.RankingMap;
 import android.service.notification.StatusBarNotification;
@@ -70,13 +72,13 @@
 import android.testing.TestableLooper;
 import android.util.ArrayMap;
 import android.util.ArraySet;
-import android.util.Pair;
 
 import androidx.annotation.NonNull;
 import androidx.test.filters.SmallTest;
 
 import com.android.internal.statusbar.IStatusBarService;
 import com.android.internal.statusbar.NotificationVisibility;
+import com.android.systemui.Flags;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.dump.LogBufferEulogizer;
@@ -129,6 +131,7 @@
     @Mock private GroupCoalescer mGroupCoalescer;
     @Spy private RecordingCollectionListener mCollectionListener;
     @Mock private CollectionReadyForBuildListener mBuildListener;
+    @Mock private NotificationDismissibilityProvider mDismissibilityProvider;
 
     @Spy private RecordingLifetimeExtender mExtender1 = new RecordingLifetimeExtender("Extender1");
     @Spy private RecordingLifetimeExtender mExtender2 = new RecordingLifetimeExtender("Extender2");
@@ -160,6 +163,7 @@
         allowTestableLooperAsMainThread();
 
         when(mEulogizer.record(any(Exception.class))).thenAnswer(i -> i.getArguments()[0]);
+        doReturn(Boolean.TRUE).when(mDismissibilityProvider).isDismissable(any());
 
         mListenerInOrder = inOrder(mCollectionListener);
 
@@ -172,7 +176,7 @@
                 mBgExecutor,
                 mEulogizer,
                 mock(DumpManager.class),
-                mock(NotificationDismissibilityProvider.class));
+                mDismissibilityProvider);
         mCollection.attach(mGroupCoalescer);
         mCollection.addCollectionListener(mCollectionListener);
         mCollection.setBuildListener(mBuildListener);
@@ -1287,8 +1291,8 @@
 
         // WHEN both notifications are manually dismissed together
         mCollection.dismissNotifications(
-                List.of(new Pair<>(entry1, defaultStats(entry1)),
-                        new Pair<>(entry2, defaultStats(entry2))));
+                List.of(entryWithDefaultStats(entry1),
+                        entryWithDefaultStats(entry2)));
 
         // THEN build list is only called one time
         verifyBuiltList(List.of(entry1, entry2));
@@ -1306,8 +1310,8 @@
         DismissedByUserStats stats1 = defaultStats(entry1);
         DismissedByUserStats stats2 = defaultStats(entry2);
         mCollection.dismissNotifications(
-                List.of(new Pair<>(entry1, defaultStats(entry1)),
-                        new Pair<>(entry2, defaultStats(entry2))));
+                List.of(entryWithDefaultStats(entry1),
+                        entryWithDefaultStats(entry2)));
 
         // THEN we send the dismissals to system server
         FakeExecutor.exhaustExecutors(mBgExecutor);
@@ -1338,8 +1342,8 @@
 
         // WHEN both notifications are manually dismissed together
         mCollection.dismissNotifications(
-                List.of(new Pair<>(entry1, defaultStats(entry1)),
-                        new Pair<>(entry2, defaultStats(entry2))));
+                List.of(entryWithDefaultStats(entry1),
+                        entryWithDefaultStats(entry2)));
 
         // THEN the entries are marked as dismissed
         assertEquals(DISMISSED, entry1.getDismissState());
@@ -1363,8 +1367,8 @@
 
         // WHEN both notifications are manually dismissed together
         mCollection.dismissNotifications(
-                List.of(new Pair<>(entry1, defaultStats(entry1)),
-                        new Pair<>(entry2, defaultStats(entry2))));
+                List.of(entryWithDefaultStats(entry1),
+                        entryWithDefaultStats(entry2)));
 
         // THEN all interceptors get checked
         verify(mInterceptor1).shouldInterceptDismissal(entry1);
@@ -1379,6 +1383,43 @@
     }
 
     @Test
+    @EnableFlags(Flags.FLAG_NOTIFICATIONS_DISMISS_PRUNED_SUMMARIES)
+    public void testDismissNotificationsIncludesPrunedParents() {
+        // GIVEN a collection with 2 groups; one has a single child, one has two.
+        mCollection.addNotificationDismissInterceptor(mInterceptor1);
+
+        NotifEvent notif1summary = mNoMan.postNotif(
+                buildNotif(TEST_PACKAGE, 1, "notif1summary").setGroup(mContext, "group1")
+                        .setGroupSummary(mContext, true));
+        NotifEvent notif1child = mNoMan.postNotif(
+                buildNotif(TEST_PACKAGE, 1, "notif1child").setGroup(mContext, "group1"));
+        NotifEvent notif2summary = mNoMan.postNotif(
+                buildNotif(TEST_PACKAGE2, 2, "notif2summary").setGroup(mContext, "group2")
+                        .setGroupSummary(mContext, true));
+        NotifEvent notif2child1 = mNoMan.postNotif(
+                buildNotif(TEST_PACKAGE2, 2, "notif2child1").setGroup(mContext, "group2"));
+        NotifEvent notif2child2 = mNoMan.postNotif(
+                buildNotif(TEST_PACKAGE2, 2, "notif2child2").setGroup(mContext, "group2"));
+        NotificationEntry entry1summary = mCollectionListener.getEntry(notif1summary.key);
+        NotificationEntry entry1child = mCollectionListener.getEntry(notif1child.key);
+        NotificationEntry entry2summary = mCollectionListener.getEntry(notif2summary.key);
+        NotificationEntry entry2child1 = mCollectionListener.getEntry(notif2child1.key);
+        NotificationEntry entry2child2 = mCollectionListener.getEntry(notif2child2.key);
+
+        // WHEN one child from each group are manually dismissed together
+        mCollection.dismissNotifications(
+                List.of(entryWithDefaultStats(entry1child),
+                        entryWithDefaultStats(entry2child1)));
+
+        // THEN the summary for the singleton child is dismissed, but not the other summary
+        verify(mInterceptor1).shouldInterceptDismissal(entry1summary);
+        verify(mInterceptor1).shouldInterceptDismissal(entry1child);
+        verify(mInterceptor1, never()).shouldInterceptDismissal(entry2summary);
+        verify(mInterceptor1).shouldInterceptDismissal(entry2child1);
+        verify(mInterceptor1, never()).shouldInterceptDismissal(entry2child2);
+    }
+
+    @Test
     public void testDismissAllNotificationsCallsRebuildOnce() {
         // GIVEN a collection with a couple notifications
         NotifEvent notif1 = mNoMan.postNotif(buildNotif(TEST_PACKAGE, 47, "myTag"));
@@ -1764,6 +1805,10 @@
                 NotificationVisibility.obtain(entry.getKey(), 7, 2, true));
     }
 
+    private static EntryWithDismissStats entryWithDefaultStats(NotificationEntry entry) {
+        return new EntryWithDismissStats(entry, defaultStats(entry));
+    }
+
     private CollectionEvent postNotif(NotificationEntryBuilder builder) {
         clearInvocations(mCollectionListener);
         NotifEvent rawEvent = mNoMan.postNotif(builder);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
index e3e2491..e313a05 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
@@ -71,7 +71,6 @@
 import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper;
 import com.android.systemui.statusbar.notification.shared.NotificationContentAlphaOptimization;
 import com.android.systemui.statusbar.notification.stack.NotificationChildrenContainer;
-import com.android.systemui.statusbar.phone.ExpandHeadsUpOnInlineReply;
 import com.android.systemui.statusbar.phone.KeyguardBypassController;
 
 import org.junit.Assert;
@@ -863,149 +862,6 @@
     }
 
     @Test
-    @EnableFlags(ExpandHeadsUpOnInlineReply.FLAG_NAME)
-    public void isExpanded_HUNsystemExpandedTrueForPinned_notExpanded() throws Exception {
-        // GIVEN
-        final ExpandableNotificationRow row = mNotificationTestHelper.createRow();
-        row.setOnKeyguard(false);
-        row.setSystemExpanded(true);
-        row.setPinned(true);
-        row.setHeadsUp(true);
-
-        // THEN
-        assertThat(row.isExpanded()).isFalse();
-    }
-
-    @Test
-    @EnableFlags(ExpandHeadsUpOnInlineReply.FLAG_NAME)
-    public void isExpanded_HUNsystemExpandedTrueForNotPinned_expanded() throws Exception {
-        // GIVEN
-        final ExpandableNotificationRow row = mNotificationTestHelper.createRow();
-        row.setOnKeyguard(false);
-        row.setSystemExpanded(true);
-        row.setPinned(false);
-        row.setHeadsUp(true);
-
-        // THEN
-        assertThat(row.isExpanded()).isTrue();
-    }
-
-    @Test
-    @EnableFlags(ExpandHeadsUpOnInlineReply.FLAG_NAME)
-    public void isExpanded_HUNDisappearingsystemExpandedTrueForPinned_notExpanded()
-            throws Exception {
-        // GIVEN
-        final ExpandableNotificationRow row = mNotificationTestHelper.createRow();
-        row.setOnKeyguard(false);
-        row.setSystemExpanded(true);
-        row.setPinned(true);
-        row.setHeadsUpAnimatingAway(true);
-
-        // THEN
-        assertThat(row.isExpanded()).isFalse();
-    }
-
-    @Test
-    @EnableFlags(ExpandHeadsUpOnInlineReply.FLAG_NAME)
-    public void isExpanded_HUNDisappearingsystemExpandedTrueForNotPinned_expanded()
-            throws Exception {
-        // GIVEN
-        final ExpandableNotificationRow row = mNotificationTestHelper.createRow();
-        row.setOnKeyguard(false);
-        row.setSystemExpanded(true);
-        row.setPinned(false);
-        row.setHeadsUpAnimatingAway(true);
-
-        // THEN
-        assertThat(row.isExpanded()).isTrue();
-    }
-
-    @Test
-    @EnableFlags(ExpandHeadsUpOnInlineReply.FLAG_NAME)
-    public void isExpanded_userExpandedTrueForHeadsUp_expanded() throws Exception {
-        // GIVEN
-        final ExpandableNotificationRow row = mNotificationTestHelper.createRow();
-        row.setOnKeyguard(false);
-        row.setSystemExpanded(true);
-        row.setHeadsUpAnimatingAway(true);
-        row.setUserExpanded(true);
-
-        // THEN
-        assertThat(row.isExpanded()).isTrue();
-    }
-    @Test
-    @EnableFlags(ExpandHeadsUpOnInlineReply.FLAG_NAME)
-    public void isExpanded_userExpandedTrueForHeadsUpDisappearRunning_expanded() throws Exception {
-        // GIVEN
-        final ExpandableNotificationRow row = mNotificationTestHelper.createRow();
-        row.setOnKeyguard(false);
-        row.setSystemExpanded(true);
-        row.setHeadsUpAnimatingAway(true);
-        row.setUserExpanded(true);
-
-        // THEN
-        assertThat(row.isExpanded()).isTrue();
-    }
-
-    @Test
-    @EnableFlags(ExpandHeadsUpOnInlineReply.FLAG_NAME)
-    public void isExpanded_userExpandedFalseForHeadsUp_notExpanded() throws Exception {
-        // GIVEN
-        final ExpandableNotificationRow row = mNotificationTestHelper.createRow();
-        row.setOnKeyguard(false);
-        row.setSystemExpanded(true);
-        row.setHeadsUpAnimatingAway(true);
-        row.setUserExpanded(false);
-
-        // THEN
-        assertThat(row.isExpanded()).isFalse();
-    }
-    @Test
-    @EnableFlags(ExpandHeadsUpOnInlineReply.FLAG_NAME)
-    public void isExpanded_userExpandedFalseForHeadsUpDisappearRunning_notExpanded()
-            throws Exception {
-        // GIVEN
-        final ExpandableNotificationRow row = mNotificationTestHelper.createRow();
-        row.setOnKeyguard(false);
-        row.setSystemExpanded(true);
-        row.setHeadsUpAnimatingAway(true);
-        row.setUserExpanded(false);
-
-        // THEN
-        assertThat(row.isExpanded()).isFalse();
-    }
-
-    @Test
-    @EnableFlags(ExpandHeadsUpOnInlineReply.FLAG_NAME)
-    public void isExpanded_HUNexpandedWhenPinningTrue_expanded() throws Exception {
-        // GIVEN
-        final ExpandableNotificationRow row = mNotificationTestHelper.createRow();
-        row.setOnKeyguard(false);
-        row.setSystemExpanded(true);
-        row.setHeadsUp(true);
-        row.setPinned(true);
-
-        // WHEN
-        row.expandNotification();
-
-        // THEN
-        assertThat(row.isExpanded()).isTrue();
-    }
-
-    @Test
-    @EnableFlags(ExpandHeadsUpOnInlineReply.FLAG_NAME)
-    public void isExpanded_HUNexpandedWhenPinningFalse_notExpanded() throws Exception {
-        // GIVEN
-        final ExpandableNotificationRow row = mNotificationTestHelper.createRow();
-        row.setOnKeyguard(false);
-        row.setSystemExpanded(false);
-        row.setHeadsUp(true);
-        row.setPinned(true);
-
-        // THEN
-        assertThat(row.isExpanded()).isFalse();
-    }
-    @Test
     public void onDisappearAnimationFinished_shouldSetFalse_headsUpAnimatingAway()
             throws Exception {
         final ExpandableNotificationRow row = mNotificationTestHelper.createRow();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
index fdfc253..06e8b1c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
@@ -91,6 +91,7 @@
 import com.android.systemui.statusbar.notification.emptyshade.shared.ModesEmptyShadeFix;
 import com.android.systemui.statusbar.notification.emptyshade.ui.view.EmptyShadeView;
 import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor;
+import com.android.systemui.statusbar.notification.footer.shared.NotifRedesignFooter;
 import com.android.systemui.statusbar.notification.footer.ui.view.FooterView;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.ExpandableView;
@@ -559,7 +560,7 @@
     }
 
     @Test
-    @DisableFlags(FooterViewRefactor.FLAG_NAME)
+    @DisableFlags({FooterViewRefactor.FLAG_NAME, NotifRedesignFooter.FLAG_NAME})
     public void manageNotifications_visible() {
         FooterView view = mock(FooterView.class);
         mStackScroller.setFooterView(view);
@@ -572,7 +573,7 @@
     }
 
     @Test
-    @DisableFlags(FooterViewRefactor.FLAG_NAME)
+    @DisableFlags({FooterViewRefactor.FLAG_NAME, NotifRedesignFooter.FLAG_NAME})
     public void clearAll_visible() {
         FooterView view = mock(FooterView.class);
         mStackScroller.setFooterView(view);
@@ -585,7 +586,7 @@
     }
 
     @Test
-    @DisableFlags(FooterViewRefactor.FLAG_NAME)
+    @DisableFlags({FooterViewRefactor.FLAG_NAME, NotifRedesignFooter.FLAG_NAME})
     public void testInflateFooterView() {
         mStackScroller.inflateFooterView();
         ArgumentCaptor<FooterView> captor = ArgumentCaptor.forClass(FooterView.class);
@@ -596,7 +597,7 @@
     }
 
     @Test
-    @DisableFlags(FooterViewRefactor.FLAG_NAME)
+    @DisableFlags({FooterViewRefactor.FLAG_NAME, NotifRedesignFooter.FLAG_NAME})
     public void testUpdateFooter_noNotifications() {
         setBarStateForTest(StatusBarState.SHADE);
         mStackScroller.setCurrentUserSetup(true);
@@ -608,7 +609,7 @@
     }
 
     @Test
-    @DisableFlags(FooterViewRefactor.FLAG_NAME)
+    @DisableFlags({FooterViewRefactor.FLAG_NAME, NotifRedesignFooter.FLAG_NAME})
     public void testUpdateFooter_remoteInput() {
         setBarStateForTest(StatusBarState.SHADE);
         mStackScroller.setCurrentUserSetup(true);
@@ -625,7 +626,7 @@
     }
 
     @Test
-    @DisableFlags(FooterViewRefactor.FLAG_NAME)
+    @DisableFlags({FooterViewRefactor.FLAG_NAME, NotifRedesignFooter.FLAG_NAME})
     public void testUpdateFooter_withoutNotifications() {
         setBarStateForTest(StatusBarState.SHADE);
         mStackScroller.setCurrentUserSetup(true);
@@ -641,7 +642,7 @@
     }
 
     @Test
-    @DisableFlags(FooterViewRefactor.FLAG_NAME)
+    @DisableFlags({FooterViewRefactor.FLAG_NAME, NotifRedesignFooter.FLAG_NAME})
     public void testUpdateFooter_oneClearableNotification() {
         setBarStateForTest(StatusBarState.SHADE);
         mStackScroller.setCurrentUserSetup(true);
@@ -657,7 +658,7 @@
     }
 
     @Test
-    @DisableFlags(FooterViewRefactor.FLAG_NAME)
+    @DisableFlags({FooterViewRefactor.FLAG_NAME, NotifRedesignFooter.FLAG_NAME})
     public void testUpdateFooter_withoutHistory() {
         setBarStateForTest(StatusBarState.SHADE);
         mStackScroller.setCurrentUserSetup(true);
@@ -674,7 +675,7 @@
     }
 
     @Test
-    @DisableFlags(FooterViewRefactor.FLAG_NAME)
+    @DisableFlags({FooterViewRefactor.FLAG_NAME, NotifRedesignFooter.FLAG_NAME})
     public void testUpdateFooter_oneClearableNotification_beforeUserSetup() {
         setBarStateForTest(StatusBarState.SHADE);
         mStackScroller.setCurrentUserSetup(false);
@@ -690,7 +691,7 @@
     }
 
     @Test
-    @DisableFlags(FooterViewRefactor.FLAG_NAME)
+    @DisableFlags({FooterViewRefactor.FLAG_NAME, NotifRedesignFooter.FLAG_NAME})
     public void testUpdateFooter_oneNonClearableNotification() {
         setBarStateForTest(StatusBarState.SHADE);
         mStackScroller.setCurrentUserSetup(true);
@@ -1181,7 +1182,7 @@
     }
 
     @Test
-    @DisableFlags(FooterViewRefactor.FLAG_NAME)
+    @DisableFlags({FooterViewRefactor.FLAG_NAME, NotifRedesignFooter.FLAG_NAME})
     public void hasFilteredOutSeenNotifs_updateFooter() {
         mStackScroller.setCurrentUserSetup(true);
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
index c639b3a..33a4b7e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
@@ -1978,6 +1978,17 @@
     }
 
     @Test
+    public void primaryBouncerToGoneOnFinishCallsLightBarController() {
+        reset(mLightBarController);
+        mScrimController.mBouncerToGoneTransition.accept(
+                new TransitionStep(KeyguardState.PRIMARY_BOUNCER, KeyguardState.GONE, 0f,
+                        TransitionState.FINISHED, "ScrimControllerTest"));
+
+        verify(mLightBarController).setScrimState(
+                any(ScrimState.class), anyFloat(), any(GradientColors.class));
+    }
+
+    @Test
     public void testDoNotAnimateChangeIfOccludeAnimationPlaying() {
         mScrimController.setOccludeAnimationPlaying(true);
         mScrimController.legacyTransitionTo(ScrimState.UNLOCKED);
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 1ceb20a..c4c2aa7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -2518,6 +2518,21 @@
                 eq(BubbleLogger.Event.BUBBLE_BAR_BUBBLE_DISMISSED_DRAG_BUBBLE));
     }
 
+    @EnableFlags(FLAG_ENABLE_BUBBLE_BAR)
+    @Test
+    public void testEventLogging_bubbleBar_dragBarToDismiss() {
+        mBubbleProperties.mIsBubbleBarEnabled = true;
+        mPositioner.setIsLargeScreen(true);
+
+        // Not a user gesture, should not log an event
+        mBubbleController.removeAllBubbles(Bubbles.DISMISS_NO_LONGER_BUBBLE);
+        verify(mBubbleLogger, never()).log(BubbleLogger.Event.BUBBLE_BAR_DISMISSED_DRAG_BAR);
+
+        // Dismiss via user gesture, log an event
+        mBubbleController.removeAllBubbles(Bubbles.DISMISS_USER_GESTURE);
+        verify(mBubbleLogger).log(BubbleLogger.Event.BUBBLE_BAR_DISMISSED_DRAG_BAR);
+    }
+
     /** Creates a bubble using the userId and package. */
     private Bubble createBubble(int userId, String pkg) {
         final UserHandle userHandle = new UserHandle(userId);
diff --git a/packages/SystemUI/tests/utils/src/android/os/LooperKosmos.kt b/packages/SystemUI/tests/utils/src/android/os/LooperKosmos.kt
index 4303365..3f79c59 100644
--- a/packages/SystemUI/tests/utils/src/android/os/LooperKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/android/os/LooperKosmos.kt
@@ -21,11 +21,12 @@
 import com.android.systemui.kosmos.Kosmos.Fixture
 import com.android.systemui.kosmos.testCase
 
-val Kosmos.looper by Fixture {
-    val testableLooper = TestableLooper.get(testCase)
-    checkNotNull(testableLooper) {
+val Kosmos.testableLooper: TestableLooper by Fixture {
+    checkNotNull(TestableLooper.get(testCase)) {
         "TestableLooper is null, make sure the test class is annotated with RunWithLooper"
     }
+}
+val Kosmos.looper: Looper by Fixture {
     checkNotNull(testableLooper.looper) {
         "TestableLooper.getLooper() is returning null, make sure the test class is annotated " +
             "with RunWithLooper"
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/data/repository/BouncerRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/data/repository/BouncerRepositoryKosmos.kt
index c0f8638..61698f4 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/data/repository/BouncerRepositoryKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/data/repository/BouncerRepositoryKosmos.kt
@@ -16,12 +16,16 @@
 
 package com.android.systemui.bouncer.data.repository
 
+import android.content.applicationContext
 import com.android.systemui.flags.featureFlagsClassic
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.Kosmos.Fixture
+import com.android.systemui.util.settings.fakeGlobalSettings
 
 val Kosmos.bouncerRepository by Fixture {
     BouncerRepository(
+        applicationContext = applicationContext,
         flags = featureFlagsClassic,
+        globalSettings = fakeGlobalSettings,
     )
 }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/domain/interactor/BouncerActionButtonInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/domain/interactor/BouncerActionButtonInteractorKosmos.kt
index 77ec838..3087d01 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/domain/interactor/BouncerActionButtonInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/domain/interactor/BouncerActionButtonInteractorKosmos.kt
@@ -23,7 +23,6 @@
 import com.android.internal.util.emergencyAffordanceManager
 import com.android.systemui.authentication.domain.interactor.authenticationInteractor
 import com.android.systemui.bouncer.data.repository.emergencyServicesRepository
-import com.android.systemui.haptics.msdl.bouncerHapticPlayer
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.Kosmos.Fixture
 import com.android.systemui.kosmos.testDispatcher
@@ -53,6 +52,5 @@
         metricsLogger = metricsLogger,
         dozeLogger = mock(),
         sceneInteractor = { sceneInteractor },
-        bouncerHapticPlayer,
     )
 }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorKosmos.kt
index 4394847..d27ecce 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorKosmos.kt
@@ -20,6 +20,7 @@
 import com.android.systemui.authentication.domain.interactor.authenticationInteractor
 import com.android.systemui.bouncer.data.repository.bouncerRepository
 import com.android.systemui.classifier.domain.interactor.falsingInteractor
+import com.android.systemui.common.ui.domain.interactor.configurationInteractor
 import com.android.systemui.deviceentry.domain.interactor.deviceEntryFaceAuthInteractor
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.Kosmos.Fixture
@@ -39,5 +40,6 @@
         uiEventLogger = uiEventLogger,
         sessionTracker = sessionTracker,
         sceneBackInteractor = sceneBackInteractor,
+        configurationInteractor = configurationInteractor,
     )
 }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelKosmos.kt
index c77d0aa..e0d1d16 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelKosmos.kt
@@ -62,6 +62,7 @@
         passwordViewModelFactory = passwordBouncerViewModelFactory,
         bouncerHapticPlayer = bouncerHapticPlayer,
         keyguardMediaKeyInteractor = keyguardMediaKeyInteractor,
+        bouncerActionButtonInteractor = bouncerActionButtonInteractor,
     )
 }
 
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/brightness/ui/viewmodel/BrightnessSliderViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/brightness/ui/viewmodel/BrightnessSliderViewModelKosmos.kt
index 6889b8a..52cdbed 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/brightness/ui/viewmodel/BrightnessSliderViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/brightness/ui/viewmodel/BrightnessSliderViewModelKosmos.kt
@@ -20,14 +20,19 @@
 import com.android.systemui.brightness.domain.interactor.screenBrightnessInteractor
 import com.android.systemui.haptics.slider.sliderHapticsViewModelFactory
 import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.settings.brightness.domain.interactor.brightnessMirrorShowingInteractor
 
-val Kosmos.brightnessSliderViewModel: BrightnessSliderViewModel by
+val Kosmos.brightnessSliderViewModelFactory: BrightnessSliderViewModel.Factory by
     Kosmos.Fixture {
-        BrightnessSliderViewModel(
-            screenBrightnessInteractor = screenBrightnessInteractor,
-            brightnessPolicyEnforcementInteractor = brightnessPolicyEnforcementInteractor,
-            applicationScope = applicationCoroutineScope,
-            hapticsViewModelFactory = sliderHapticsViewModelFactory,
-        )
+        object : BrightnessSliderViewModel.Factory {
+            override fun create(allowsMirroring: Boolean): BrightnessSliderViewModel {
+                return BrightnessSliderViewModel(
+                    screenBrightnessInteractor = screenBrightnessInteractor,
+                    brightnessPolicyEnforcementInteractor = brightnessPolicyEnforcementInteractor,
+                    hapticsViewModelFactory = sliderHapticsViewModelFactory,
+                    brightnessMirrorShowingInteractor = brightnessMirrorShowingInteractor,
+                    supportsMirroring = allowsMirroring,
+                )
+            }
+        }
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/broadcast/BroadcastDispatcherKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/broadcast/BroadcastDispatcherKosmos.kt
index 7207948..b2289a1 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/broadcast/BroadcastDispatcherKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/broadcast/BroadcastDispatcherKosmos.kt
@@ -16,19 +16,21 @@
 
 package com.android.systemui.broadcast
 
+import com.android.systemui.SysuiTestableContext
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.util.mockito.mock
 
+var Kosmos.broadcastDispatcherContext: SysuiTestableContext by Kosmos.Fixture { mock() }
 val Kosmos.broadcastDispatcher by
     Kosmos.Fixture {
         FakeBroadcastDispatcher(
-            context = mock(),
+            context = broadcastDispatcherContext,
             mainExecutor = mock(),
             broadcastRunningLooper = mock(),
             broadcastRunningExecutor = mock(),
             dumpManager = mock(),
             logger = mock(),
             userTracker = mock(),
-            shouldFailOnLeakedReceiver = false
+            shouldFailOnLeakedReceiver = false,
         )
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/FakeDisplayWindowPropertiesRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/FakeDisplayWindowPropertiesRepository.kt
index 9282f27..92dc897 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/FakeDisplayWindowPropertiesRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/FakeDisplayWindowPropertiesRepository.kt
@@ -31,6 +31,7 @@
                     windowType = windowType,
                     context = mock(),
                     windowManager = mock(),
+                    layoutInflater = mock(),
                 )
                 .also { properties.put(displayId, windowType, it) }
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
index 0878649..693ec79 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
@@ -131,6 +131,10 @@
     override val shortcutAbsoluteTop: StateFlow<Float>
         get() = _shortcutAbsoluteTop.asStateFlow()
 
+    private val _notificationStackAbsoluteBottom = MutableStateFlow(0F)
+    override val notificationStackAbsoluteBottom: StateFlow<Float>
+        get() = _notificationStackAbsoluteBottom.asStateFlow()
+
     private val _isKeyguardEnabled = MutableStateFlow(true)
     override val isKeyguardEnabled: StateFlow<Boolean> = _isKeyguardEnabled.asStateFlow()
 
@@ -294,6 +298,10 @@
         _shortcutAbsoluteTop.value = top
     }
 
+    override fun setNotificationStackAbsoluteBottom(bottom: Float) {
+        _notificationStackAbsoluteBottom.value = bottom
+    }
+
     override fun setCanIgnoreAuthAndReturnToGone(canWake: Boolean) {
         _canIgnoreAuthAndReturnToGone.value = canWake
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt
index 70b4f79..4976cc2 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt
@@ -18,6 +18,7 @@
 package com.android.systemui.keyguard.data.repository
 
 import android.annotation.FloatRange
+import com.android.systemui.Flags.transitionRaceCondition
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.keyguard.shared.model.TransitionInfo
@@ -88,6 +89,13 @@
             )
         )
     override var currentTransitionInfoInternal = _currentTransitionInfo.asStateFlow()
+    override var currentTransitionInfo =
+        TransitionInfo(
+            ownerName = "",
+            from = KeyguardState.OFF,
+            to = KeyguardState.LOCKSCREEN,
+            animator = null,
+        )
 
     init {
         // Seed with a FINISHED transition in OFF, same as the real repository.
@@ -261,8 +269,13 @@
         validateStep: Boolean = true,
     ) {
         if (step.transitionState == TransitionState.STARTED) {
-            _currentTransitionInfo.value =
-                TransitionInfo(from = step.from, to = step.to, animator = null, ownerName = "")
+            if (transitionRaceCondition()) {
+                currentTransitionInfo =
+                    TransitionInfo(from = step.from, to = step.to, animator = null, ownerName = "")
+            } else {
+                _currentTransitionInfo.value =
+                    TransitionInfo(from = step.from, to = step.to, animator = null, ownerName = "")
+            }
         }
 
         _transitions.replayCache.last().let { lastStep ->
@@ -308,7 +321,11 @@
     }
 
     override suspend fun startTransition(info: TransitionInfo): UUID? {
-        _currentTransitionInfo.value = info
+        if (transitionRaceCondition()) {
+            currentTransitionInfo = info
+        } else {
+            _currentTransitionInfo.value = info
+        }
 
         if (sendTransitionStepsOnStartTransition) {
             sendTransitionSteps(from = info.from, to = info.to, testScope = testScope)
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractorKosmos.kt
index 700d7e9..ff0f13e 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractorKosmos.kt
@@ -17,8 +17,6 @@
 package com.android.systemui.keyguard.domain.interactor
 
 import com.android.systemui.communal.domain.interactor.communalSceneInteractor
-import com.android.systemui.keyguard.data.repository.biometricSettingsRepository
-import com.android.systemui.keyguard.data.repository.keyguardRepository
 import com.android.systemui.keyguard.data.repository.keyguardTransitionRepository
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.applicationCoroutineScope
@@ -39,8 +37,6 @@
             powerInteractor = powerInteractor,
             communalSceneInteractor = communalSceneInteractor,
             keyguardOcclusionInteractor = keyguardOcclusionInteractor,
-            biometricSettingsRepository = biometricSettingsRepository,
-            keyguardRepository = keyguardRepository,
-            keyguardEnabledInteractor = keyguardEnabledInteractor,
+            keyguardLockWhileAwakeInteractor = keyguardLockWhileAwakeInteractor,
         )
     }
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 2f13ba4..bd841ab 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
@@ -16,11 +16,14 @@
 
 package com.android.systemui.keyguard.domain.interactor
 
+import com.android.keyguard.logging.KeyguardLogger
 import com.android.systemui.bouncer.domain.interactor.alternateBouncerInteractor
+import com.android.systemui.bouncer.domain.interactor.primaryBouncerInteractor
 import com.android.systemui.deviceentry.domain.interactor.deviceUnlockedInteractor
 import com.android.systemui.keyguard.data.repository.keyguardRepository
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testScope
+import com.android.systemui.log.logcatLogBuffer
 import com.android.systemui.power.domain.interactor.powerInteractor
 import com.android.systemui.scene.domain.interactor.sceneInteractor
 import com.android.systemui.shade.domain.interactor.shadeInteractor
@@ -40,5 +43,7 @@
             shadeInteractor = { shadeInteractor },
             keyguardInteractor = { keyguardInteractor },
             sceneInteractor = { sceneInteractor },
+            keyguardLogger = KeyguardLogger(logcatLogBuffer("keyguard-logger-for-test")),
+            primaryBouncerInteractor = primaryBouncerInteractor,
         )
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeUserActionsViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardLockWhileAwakeInteractorKosmos.kt
similarity index 65%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeUserActionsViewModelKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardLockWhileAwakeInteractorKosmos.kt
index 06592b1..39236c7 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeUserActionsViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardLockWhileAwakeInteractorKosmos.kt
@@ -14,13 +14,15 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.ui.viewmodel
+package com.android.systemui.keyguard.domain.interactor
 
+import com.android.systemui.keyguard.data.repository.biometricSettingsRepository
 import com.android.systemui.kosmos.Kosmos
 
-val Kosmos.quickSettingsShadeUserActionsViewModel: QuickSettingsShadeUserActionsViewModel by
+val Kosmos.keyguardLockWhileAwakeInteractor by
     Kosmos.Fixture {
-        QuickSettingsShadeUserActionsViewModel(
-            quickSettingsContainerViewModel = quickSettingsContainerViewModel,
+        KeyguardLockWhileAwakeInteractor(
+            biometricSettingsRepository = biometricSettingsRepository,
+            keyguardEnabledInteractor = keyguardEnabledInteractor,
         )
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/GeneralKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/GeneralKosmos.kt
index f8df707..a9a80b5 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/GeneralKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/GeneralKosmos.kt
@@ -8,9 +8,26 @@
 import kotlinx.coroutines.test.UnconfinedTestDispatcher
 
 var Kosmos.testDispatcher by Fixture { StandardTestDispatcher() }
-var Kosmos.unconfinedTestDispatcher by Fixture { UnconfinedTestDispatcher() }
+
+/**
+ * Force this Kosmos to use a [StandardTestDispatcher], regardless of the current Kosmos default. In
+ * short, no launch blocks will be run on this dispatcher until `TestCoroutineScheduler.runCurrent`
+ * is called. See [StandardTestDispatcher] for details.
+ *
+ * For details on this migration, see http://go/thetiger
+ */
+fun Kosmos.useStandardTestDispatcher() = apply { testDispatcher = StandardTestDispatcher() }
+
+/**
+ * Force this Kosmos to use an [UnconfinedTestDispatcher], regardless of the current Kosmos default.
+ * In short, launch blocks will be executed eagerly without waiting for
+ * `TestCoroutineScheduler.runCurrent`. See [UnconfinedTestDispatcher] for details.
+ *
+ * For details on this migration, see http://go/thetiger
+ */
+fun Kosmos.useUnconfinedTestDispatcher() = apply { testDispatcher = UnconfinedTestDispatcher() }
+
 var Kosmos.testScope by Fixture { TestScope(testDispatcher) }
-var Kosmos.unconfinedTestScope by Fixture { TestScope(unconfinedTestDispatcher) }
 var Kosmos.applicationCoroutineScope by Fixture { testScope.backgroundScope }
 var Kosmos.testCase: SysuiTestCase by Fixture()
 var Kosmos.backgroundCoroutineContext: CoroutineContext by Fixture {
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/plugins/FakeVolumeDialogController.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/plugins/FakeVolumeDialogController.kt
index b45120e..43eb93e 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/plugins/FakeVolumeDialogController.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/plugins/FakeVolumeDialogController.kt
@@ -43,30 +43,36 @@
     private var state = VolumeDialogController.State()
 
     override fun setActiveStream(stream: Int) {
-        // ensure streamState existence for the active stream
-        state.states.getOrElse(stream) {
-            VolumeDialogController.StreamState().also { streamState ->
-                state.states.put(stream, streamState)
-            }
-        }
-        state.activeStream = stream
-    }
-
-    override fun setStreamVolume(stream: Int, userLevel: Int) {
-        val streamState =
-            state.states.getOrElse(stream) {
+        updateState {
+            // ensure streamState existence for the active stream`
+            states.getOrElse(stream) {
                 VolumeDialogController.StreamState().also { streamState ->
                     state.states.put(stream, streamState)
                 }
             }
-        streamState.level = userLevel.coerceIn(streamState.levelMin, streamState.levelMax)
+            activeStream = stream
+        }
+    }
+
+    override fun setStreamVolume(stream: Int, userLevel: Int) {
+        updateState {
+            val streamState =
+                states.getOrElse(stream) {
+                    VolumeDialogController.StreamState().also { streamState ->
+                        states.put(stream, streamState)
+                    }
+                }
+            streamState.level = userLevel.coerceIn(streamState.levelMin, streamState.levelMax)
+        }
     }
 
     override fun setRingerMode(ringerModeNormal: Int, external: Boolean) {
-        if (external) {
-            state.ringerModeExternal = ringerModeNormal
-        } else {
-            state.ringerModeInternal = ringerModeNormal
+        updateState {
+            if (external) {
+                ringerModeExternal = ringerModeNormal
+            } else {
+                ringerModeInternal = ringerModeNormal
+            }
         }
     }
 
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModelKosmos.kt
index dff5625..462b408 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModelKosmos.kt
@@ -26,7 +26,7 @@
 import com.android.systemui.qs.footerActionsViewModelFactory
 import com.android.systemui.qs.panels.domain.interactor.tileSquishinessInteractor
 import com.android.systemui.qs.panels.ui.viewmodel.paginatedGridViewModel
-import com.android.systemui.qs.ui.viewmodel.quickSettingsContainerViewModel
+import com.android.systemui.qs.ui.viewmodel.quickSettingsContainerViewModelFactory
 import com.android.systemui.shade.largeScreenHeaderHelper
 import com.android.systemui.shade.transition.largeScreenShadeInterpolator
 import com.android.systemui.statusbar.disableflags.data.repository.disableFlagsRepository
@@ -41,7 +41,7 @@
                 lifecycleScope: LifecycleCoroutineScope
             ): QSFragmentComposeViewModel {
                 return QSFragmentComposeViewModel(
-                    quickSettingsContainerViewModel,
+                    quickSettingsContainerViewModelFactory,
                     mainResources,
                     footerActionsViewModelFactory,
                     footerActionsController,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsContainerViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsContainerViewModelKosmos.kt
index 779634d..6ded751 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsContainerViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsContainerViewModelKosmos.kt
@@ -16,18 +16,25 @@
 
 package com.android.systemui.qs.ui.viewmodel
 
-import com.android.systemui.brightness.ui.viewmodel.brightnessSliderViewModel
+import com.android.systemui.brightness.ui.viewmodel.brightnessSliderViewModelFactory
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.qs.panels.ui.viewmodel.editModeViewModel
 import com.android.systemui.qs.panels.ui.viewmodel.quickQuickSettingsViewModel
 import com.android.systemui.qs.panels.ui.viewmodel.tileGridViewModel
 
-val Kosmos.quickSettingsContainerViewModel by
+val Kosmos.quickSettingsContainerViewModelFactory by
     Kosmos.Fixture {
-        QuickSettingsContainerViewModel(
-            brightnessSliderViewModel,
-            tileGridViewModel,
-            editModeViewModel,
-            quickQuickSettingsViewModel,
-        )
+        object : QuickSettingsContainerViewModel.Factory {
+            override fun create(
+                supportsBrightnessMirroring: Boolean
+            ): QuickSettingsContainerViewModel {
+                return QuickSettingsContainerViewModel(
+                    brightnessSliderViewModelFactory,
+                    supportsBrightnessMirroring,
+                    tileGridViewModel,
+                    editModeViewModel,
+                    quickQuickSettingsViewModel,
+                )
+            }
+        }
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayActionsViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayActionsViewModelKosmos.kt
index 6ced8c3..a1dbeaa 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayActionsViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayActionsViewModelKosmos.kt
@@ -18,6 +18,7 @@
 
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.Kosmos.Fixture
+import com.android.systemui.qs.panels.ui.viewmodel.editModeViewModel
 import com.android.systemui.qs.ui.adapter.FakeQSSceneAdapter
 import com.android.systemui.util.mockito.mock
 
@@ -25,5 +26,5 @@
 
 val Kosmos.quickSettingsShadeOverlayActionsViewModel:
     QuickSettingsShadeOverlayActionsViewModel by Fixture {
-    QuickSettingsShadeOverlayActionsViewModel(quickSettingsContainerViewModel)
+    QuickSettingsShadeOverlayActionsViewModel(editModeViewModel)
 }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayContentViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayContentViewModelKosmos.kt
index 6540ed6..0462848 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayContentViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayContentViewModelKosmos.kt
@@ -27,6 +27,6 @@
             shadeInteractor = shadeInteractor,
             sceneInteractor = sceneInteractor,
             shadeHeaderViewModelFactory = shadeHeaderViewModelFactory,
-            quickSettingsContainerViewModel = quickSettingsContainerViewModel,
+            quickSettingsContainerViewModelFactory = quickSettingsContainerViewModelFactory,
         )
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneContentViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneContentViewModelKosmos.kt
deleted file mode 100644
index cd1704c..0000000
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneContentViewModelKosmos.kt
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.qs.ui.viewmodel
-
-import com.android.systemui.kosmos.Kosmos
-
-val Kosmos.quickSettingsShadeSceneContentViewModel: QuickSettingsShadeSceneContentViewModel by
-    Kosmos.Fixture {
-        QuickSettingsShadeSceneContentViewModel(
-            quickSettingsContainerViewModel = quickSettingsContainerViewModel,
-        )
-    }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/startable/ShadeStartableKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/startable/ShadeStartableKosmos.kt
index a1f157f1..10534a0 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/startable/ShadeStartableKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/startable/ShadeStartableKosmos.kt
@@ -40,7 +40,7 @@
 val Kosmos.shadeStartable by Fixture {
     ShadeStartable(
         applicationScope = applicationCoroutineScope,
-        applicationContext = applicationContext,
+        context = applicationContext,
         touchLog = mock<LogBuffer>(),
         configurationRepository = configurationRepository,
         shadeRepository = shadeRepository,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeUserActionsViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/notification/domain/interactor/StatusBarNotificationChipsInteractorKosmos.kt
similarity index 68%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeUserActionsViewModelKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/notification/domain/interactor/StatusBarNotificationChipsInteractorKosmos.kt
index 06592b1..74c7611 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeUserActionsViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/notification/domain/interactor/StatusBarNotificationChipsInteractorKosmos.kt
@@ -14,13 +14,9 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.ui.viewmodel
+package com.android.systemui.statusbar.chips.notification.domain.interactor
 
 import com.android.systemui.kosmos.Kosmos
 
-val Kosmos.quickSettingsShadeUserActionsViewModel: QuickSettingsShadeUserActionsViewModel by
-    Kosmos.Fixture {
-        QuickSettingsShadeUserActionsViewModel(
-            quickSettingsContainerViewModel = quickSettingsContainerViewModel,
-        )
-    }
+val Kosmos.statusBarNotificationChipsInteractor: StatusBarNotificationChipsInteractor by
+    Kosmos.Fixture { StatusBarNotificationChipsInteractor() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelKosmos.kt
index af24c37..68b28ad 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelKosmos.kt
@@ -17,7 +17,15 @@
 package com.android.systemui.statusbar.chips.notification.ui.viewmodel
 
 import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.statusbar.chips.notification.domain.interactor.statusBarNotificationChipsInteractor
 import com.android.systemui.statusbar.notification.domain.interactor.activeNotificationsInteractor
 
 val Kosmos.notifChipsViewModel: NotifChipsViewModel by
-    Kosmos.Fixture { NotifChipsViewModel(activeNotificationsInteractor) }
+    Kosmos.Fixture {
+        NotifChipsViewModel(
+            applicationCoroutineScope,
+            activeNotificationsInteractor,
+            statusBarNotificationChipsInteractor,
+        )
+    }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractorKosmos.kt
index 83fc3e9..b1e9d89 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractorKosmos.kt
@@ -23,7 +23,6 @@
 import com.android.systemui.deviceentry.domain.interactor.deviceEntryUdfpsInteractor
 import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
 import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.shade.domain.interactor.shadeInteractor
 import com.android.systemui.shade.largeScreenHeaderHelper
 import com.android.systemui.statusbar.policy.splitShadeStateController
 import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -33,7 +32,6 @@
         SharedNotificationContainerInteractor(
             context = applicationContext,
             splitShadeStateController = { splitShadeStateController },
-            shadeInteractor = { shadeInteractor },
             configurationInteractor = configurationInteractor,
             keyguardInteractor = keyguardInteractor,
             deviceEntryUdfpsInteractor = deviceEntryUdfpsInteractor,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelKosmos.kt
index a25a3c0..7fbf4e4 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelKosmos.kt
@@ -16,6 +16,8 @@
 
 package com.android.systemui.statusbar.notification.stack.ui.viewmodel
 
+import android.content.applicationContext
+import com.android.systemui.common.ui.domain.interactor.configurationInteractor
 import com.android.systemui.communal.domain.interactor.communalSceneInteractor
 import com.android.systemui.dump.dumpManager
 import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
@@ -49,6 +51,7 @@
 import com.android.systemui.kosmos.Kosmos.Fixture
 import com.android.systemui.kosmos.applicationCoroutineScope
 import com.android.systemui.shade.domain.interactor.shadeInteractor
+import com.android.systemui.shade.largeScreenHeaderHelper
 import com.android.systemui.statusbar.notification.stack.domain.interactor.headsUpNotificationInteractor
 import com.android.systemui.statusbar.notification.stack.domain.interactor.notificationStackAppearanceInteractor
 import com.android.systemui.statusbar.notification.stack.domain.interactor.sharedNotificationContainerInteractor
@@ -61,6 +64,8 @@
         interactor = sharedNotificationContainerInteractor,
         dumpManager = dumpManager,
         applicationScope = applicationCoroutineScope,
+        context = applicationContext,
+        configurationInteractor = configurationInteractor,
         keyguardInteractor = keyguardInteractor,
         keyguardTransitionInteractor = keyguardTransitionInteractor,
         shadeInteractor = shadeInteractor,
@@ -94,6 +99,7 @@
         aodBurnInViewModel = aodBurnInViewModel,
         communalSceneInteractor = communalSceneInteractor,
         headsUpNotificationInteractor = { headsUpNotificationInteractor },
+        largeScreenHeaderHelperLazy = { largeScreenHeaderHelper },
         unfoldTransitionInteractor = unfoldTransitionInteractor,
     )
 }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeGlobalSettingsKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeGlobalSettingsKosmos.kt
index 73d423c..35fa2af 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeGlobalSettingsKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeGlobalSettingsKosmos.kt
@@ -19,10 +19,5 @@
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.Kosmos.Fixture
 import com.android.systemui.kosmos.testDispatcher
-import com.android.systemui.kosmos.unconfinedTestDispatcher
 
 val Kosmos.fakeGlobalSettings: FakeGlobalSettings by Fixture { FakeGlobalSettings(testDispatcher) }
-
-val Kosmos.unconfinedDispatcherFakeGlobalSettings: FakeGlobalSettings by Fixture {
-    FakeGlobalSettings(unconfinedTestDispatcher)
-}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeSettingsKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeSettingsKosmos.kt
index e1daf9b..76ef202 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeSettingsKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeSettingsKosmos.kt
@@ -19,13 +19,8 @@
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.Kosmos.Fixture
 import com.android.systemui.kosmos.testDispatcher
-import com.android.systemui.kosmos.unconfinedTestDispatcher
 import com.android.systemui.settings.userTracker
 
 val Kosmos.fakeSettings: FakeSettings by Fixture {
     FakeSettings(testDispatcher) { userTracker.userId }
 }
-
-val Kosmos.unconfinedDispatcherFakeSettings: FakeSettings by Fixture {
-    FakeSettings(unconfinedTestDispatcher) { userTracker.userId }
-}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/utils/FieldSetter.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/utils/FieldSetter.kt
new file mode 100644
index 0000000..6e2d722
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/utils/FieldSetter.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.utils
+
+import java.lang.reflect.Field
+
+object FieldSetter {
+    @JvmStatic
+    fun setField(obj: Any, fieldName: String, value: Any?) {
+        try {
+            val field = obj.javaClass.getDeclaredField(fieldName)
+            field.isAccessible = true
+            field[obj] = value
+        } catch (e: NoSuchFieldException) {
+            throw RuntimeException("Failed to set $fieldName of obj", e)
+        } catch (e: IllegalAccessException) {
+            throw RuntimeException("Failed to set $fieldName of obj", e)
+        }
+    }
+
+    @JvmStatic
+    fun setField(obj: Any?, fld: Field, value: Any?) {
+        try {
+            fld.setAccessible(true)
+            fld.set(obj, value)
+        } catch (e: IllegalAccessException) {
+            throw RuntimeException("Failed to set ${fld.getName()} of obj", e)
+        }
+    }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogVisibilityInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogVisibilityInteractorKosmos.kt
index 7376c7f..0d2aa4c 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogVisibilityInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogVisibilityInteractorKosmos.kt
@@ -18,6 +18,7 @@
 
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.plugins.volumeDialogController
 import com.android.systemui.volume.dialog.data.repository.volumeDialogVisibilityRepository
 import com.android.systemui.volume.dialog.utils.volumeTracer
 
@@ -28,5 +29,6 @@
             volumeDialogCallbacksInteractor,
             volumeTracer,
             volumeDialogVisibilityRepository,
+            volumeDialogController,
         )
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/ringer/domain/VolumeDialogRingerInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/ringer/domain/VolumeDialogRingerInteractorKosmos.kt
index c2a1544..1addd91 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/ringer/domain/VolumeDialogRingerInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/ringer/domain/VolumeDialogRingerInteractorKosmos.kt
@@ -19,6 +19,7 @@
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.applicationCoroutineScope
 import com.android.systemui.plugins.volumeDialogController
+import com.android.systemui.volume.data.repository.audioSystemRepository
 import com.android.systemui.volume.dialog.domain.interactor.volumeDialogStateInteractor
 
 val Kosmos.volumeDialogRingerInteractor by
@@ -27,5 +28,6 @@
             coroutineScope = applicationCoroutineScope,
             volumeDialogStateInteractor = volumeDialogStateInteractor,
             controller = volumeDialogController,
+            audioSystemRepository = audioSystemRepository,
         )
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/ringer/ui/viewmodel/VolumeDialogRingerDrawerViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/ringer/ui/viewmodel/VolumeDialogRingerDrawerViewModelKosmos.kt
new file mode 100644
index 0000000..db1c01a
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/ringer/ui/viewmodel/VolumeDialogRingerDrawerViewModelKosmos.kt
@@ -0,0 +1,35 @@
+/*
+ * 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.dialog.ringer.ui.viewmodel
+
+import com.android.systemui.haptics.vibratorHelper
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.volume.dialog.ringer.domain.volumeDialogRingerInteractor
+import com.android.systemui.volume.dialog.shared.volumeDialogLogger
+
+val Kosmos.volumeDialogRingerDrawerViewModel by
+    Kosmos.Fixture {
+        VolumeDialogRingerDrawerViewModel(
+            backgroundDispatcher = testDispatcher,
+            coroutineScope = applicationCoroutineScope,
+            interactor = volumeDialogRingerInteractor,
+            vibrator = vibratorHelper,
+            volumeDialogLogger = volumeDialogLogger,
+        )
+    }
diff --git a/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodRunnerState.java b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/shared/VolumeDialogLoggerKosmos.kt
similarity index 72%
copy from ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodRunnerState.java
copy to packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/shared/VolumeDialogLoggerKosmos.kt
index 83cbc52..f9d4a99 100644
--- a/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodRunnerState.java
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/shared/VolumeDialogLoggerKosmos.kt
@@ -13,10 +13,10 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package android.platform.test.ravenwood;
 
-/** Stub class. The actual implementaetion is in junit-impl-src. */
-public class RavenwoodRunnerState {
-    public RavenwoodRunnerState(RavenwoodAwareTestRunner runner) {
-    }
-}
+package com.android.systemui.volume.dialog.shared
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.log.logcatLogBuffer
+
+val Kosmos.volumeDialogLogger by Kosmos.Fixture { VolumeDialogLogger(logcatLogBuffer()) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSliderInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSliderInteractorKosmos.kt
new file mode 100644
index 0000000..423100a
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSliderInteractorKosmos.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.dialog.sliders.domain.interactor
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.plugins.volumeDialogController
+import com.android.systemui.volume.dialog.domain.interactor.volumeDialogStateInteractor
+import com.android.systemui.volume.dialog.sliders.domain.model.volumeDialogSliderType
+
+val Kosmos.volumeDialogSliderInteractor: VolumeDialogSliderInteractor by
+    Kosmos.Fixture {
+        VolumeDialogSliderInteractor(
+            volumeDialogSliderType,
+            volumeDialogStateInteractor,
+            volumeDialogController,
+        )
+    }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeUserActionsViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/domain/model/VolumeDialogSliderTypeKosmos.kt
similarity index 68%
rename from packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeUserActionsViewModelKosmos.kt
rename to packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/domain/model/VolumeDialogSliderTypeKosmos.kt
index 06592b1..cc8c1ea 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeUserActionsViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/domain/model/VolumeDialogSliderTypeKosmos.kt
@@ -14,13 +14,10 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.ui.viewmodel
+package com.android.systemui.volume.dialog.sliders.domain.model
 
+import android.media.AudioManager
 import com.android.systemui.kosmos.Kosmos
 
-val Kosmos.quickSettingsShadeUserActionsViewModel: QuickSettingsShadeUserActionsViewModel by
-    Kosmos.Fixture {
-        QuickSettingsShadeUserActionsViewModel(
-            quickSettingsContainerViewModel = quickSettingsContainerViewModel,
-        )
-    }
+var Kosmos.volumeDialogSliderType: VolumeDialogSliderType by
+    Kosmos.Fixture { VolumeDialogSliderType.Stream(AudioManager.STREAM_SYSTEM) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModelKosmos.kt
index 55f0a28..a78670d 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModelKosmos.kt
@@ -18,6 +18,7 @@
 
 import android.content.applicationContext
 import com.android.internal.logging.uiEventLogger
+import com.android.systemui.haptics.slider.sliderHapticsViewModelFactory
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.statusbar.policy.domain.interactor.zenModeInteractor
 import com.android.systemui.volume.domain.interactor.audioVolumeInteractor
@@ -40,6 +41,7 @@
                     zenModeInteractor,
                     uiEventLogger,
                     volumePanelLogger,
+                    sliderHapticsViewModelFactory,
                 )
             }
         }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/CastVolumeSliderViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/CastVolumeSliderViewModelKosmos.kt
index f0cb2cd..abd4235 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/CastVolumeSliderViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/CastVolumeSliderViewModelKosmos.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.volume.panel.component.volume.slider.ui.viewmodel
 
 import android.content.applicationContext
+import com.android.systemui.haptics.slider.sliderHapticsViewModelFactory
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.volume.mediaDeviceSessionInteractor
 import com.android.systemui.volume.panel.component.mediaoutput.shared.model.MediaDeviceSession
@@ -27,13 +28,14 @@
         object : CastVolumeSliderViewModel.Factory {
             override fun create(
                 session: MediaDeviceSession,
-                coroutineScope: CoroutineScope
+                coroutineScope: CoroutineScope,
             ): CastVolumeSliderViewModel {
                 return CastVolumeSliderViewModel(
                     session,
                     coroutineScope,
                     applicationContext,
                     mediaDeviceSessionInteractor,
+                    sliderHapticsViewModelFactory,
                 )
             }
         }
diff --git a/packages/Vcn/TEST_MAPPING b/packages/Vcn/TEST_MAPPING
new file mode 100644
index 0000000..bde88fe
--- /dev/null
+++ b/packages/Vcn/TEST_MAPPING
@@ -0,0 +1,10 @@
+{
+  "postsubmit": [
+    {
+      "name": "FrameworksVcnTests"
+    },
+    {
+      "name": "CtsVcnTestCases"
+    }
+  ]
+}
\ No newline at end of file
diff --git a/ravenwood/Android.bp b/ravenwood/Android.bp
index 9a5e623b..65ea9fe 100644
--- a/ravenwood/Android.bp
+++ b/ravenwood/Android.bp
@@ -170,7 +170,7 @@
         "hoststubgen-helper-runtime.ravenwood",
         "mockito-ravenwood-prebuilt",
     ],
-    visibility: ["//frameworks/base"],
+    visibility: [":__subpackages__"],
     jarjar_rules: ":ravenwood-services-jarjar-rules",
 }
 
diff --git a/ravenwood/TEST_MAPPING b/ravenwood/TEST_MAPPING
index 72f62c5..7fa0ef1 100644
--- a/ravenwood/TEST_MAPPING
+++ b/ravenwood/TEST_MAPPING
@@ -5,8 +5,8 @@
     { "name": "hoststubgen-test-tiny-test" },
     { "name": "hoststubgen-invoke-test" },
     { "name": "RavenwoodMockitoTest_device" },
-    // TODO(b/371215487): Re-enable when the test is fixed.
-    // { "name": "RavenwoodBivalentTest_device" },
+    { "name": "RavenwoodBivalentTest_device" },
+    { "name": "RavenwoodBivalentTest_device_ravenizer" },
 
     { "name": "RavenwoodBivalentInstTest_nonself_inst" },
     { "name": "RavenwoodBivalentInstTest_self_inst_device" },
diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodAwareTestRunner.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodAwareTestRunner.java
new file mode 100644
index 0000000..66a6890
--- /dev/null
+++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodAwareTestRunner.java
@@ -0,0 +1,404 @@
+/*
+ * 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 static com.android.ravenwood.common.RavenwoodCommonUtils.RAVENWOOD_VERBOSE_LOGGING;
+import static com.android.ravenwood.common.RavenwoodCommonUtils.ensureIsPublicVoidMethod;
+
+import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeTrue;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Bundle;
+import android.platform.test.annotations.RavenwoodTestRunnerInitializing;
+import android.platform.test.annotations.internal.InnerRunner;
+import android.platform.test.ravenwood.RavenwoodTestStats.Result;
+import android.util.Log;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import org.junit.AssumptionViolatedException;
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
+import org.junit.runner.Runner;
+import org.junit.runner.manipulation.Filter;
+import org.junit.runner.manipulation.Filterable;
+import org.junit.runner.manipulation.NoTestsRemainException;
+import org.junit.runner.notification.RunNotifier;
+import org.junit.runners.BlockJUnit4ClassRunner;
+import org.junit.runners.Suite;
+import org.junit.runners.model.Statement;
+import org.junit.runners.model.TestClass;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.InvocationTargetException;
+import java.util.function.BiConsumer;
+
+/**
+ * A test runner used for Ravenwood.
+ *
+ * It will delegate to another runner specified with {@link InnerRunner}
+ * (default = {@link BlockJUnit4ClassRunner}) with the following features.
+ * - Add a called before the inner runner gets a chance to run. This can be used to initialize
+ *   stuff used by the inner runner.
+ * - Add hook points with help from the four test rules such as {@link #sImplicitClassOuterRule},
+ *   which are also injected by the ravenizer tool.
+ *
+ * We use this runner to:
+ * - Initialize the Ravenwood environment.
+ * - Handle {@link android.platform.test.annotations.DisabledOnRavenwood}.
+ */
+public final class RavenwoodAwareTestRunner extends RavenwoodAwareTestRunnerBase {
+    public static final String TAG = "Ravenwood";
+
+    /** Scope of a hook. */
+    public enum Scope {
+        Class,
+        Instance,
+    }
+
+    /** Order of a hook. */
+    public enum Order {
+        Outer,
+        Inner,
+    }
+
+    private record HookRule(Scope scope, Order order) implements TestRule {
+        @Override
+        public Statement apply(Statement base, Description description) {
+            return getCurrentRunner().wrapWithHooks(base, description, scope, order);
+        }
+    }
+
+    // The following four rule instances will be injected to tests by the Ravenizer tool.
+    public static final TestRule sImplicitClassOuterRule = new HookRule(Scope.Class, Order.Outer);
+    public static final TestRule sImplicitClassInnerRule = new HookRule(Scope.Class, Order.Inner);
+    public static final TestRule sImplicitInstOuterRule = new HookRule(Scope.Instance, Order.Outer);
+    public static final TestRule sImplicitInstInnerRule = new HookRule(Scope.Instance, Order.Inner);
+
+    /** Keeps track of the runner on the current thread. */
+    private static final ThreadLocal<RavenwoodAwareTestRunner> sCurrentRunner = new ThreadLocal<>();
+
+    private static RavenwoodAwareTestRunner getCurrentRunner() {
+        var runner = sCurrentRunner.get();
+        if (runner == null) {
+            throw new RuntimeException("Current test runner not set!");
+        }
+        return runner;
+    }
+
+    final Class<?> mTestJavaClass;
+    private final Runner mRealRunner;
+    private TestClass mTestClass = null;
+
+    /**
+     * Stores internal states / methods associated with this runner that's only needed in
+     * junit-impl.
+     */
+    final RavenwoodRunnerState mState = new RavenwoodRunnerState(this);
+
+    /**
+     * Constructor.
+     */
+    public RavenwoodAwareTestRunner(Class<?> testClass) {
+        RavenwoodRuntimeEnvironmentController.globalInitOnce();
+        mTestJavaClass = testClass;
+
+        /*
+         * If the class has @DisabledOnRavenwood, then we'll delegate to
+         * ClassSkippingTestRunner, which simply skips it.
+         *
+         * We need to do it before instantiating TestClass for b/367694651.
+         */
+        if (!RavenwoodEnablementChecker.shouldRunClassOnRavenwood(testClass, true)) {
+            mRealRunner = new ClassSkippingTestRunner(testClass);
+            return;
+        }
+
+        mTestClass = new TestClass(testClass);
+
+        Log.v(TAG, "RavenwoodAwareTestRunner starting for " + testClass.getCanonicalName());
+
+        // This is needed to make AndroidJUnit4ClassRunner happy.
+        InstrumentationRegistry.registerInstance(null, Bundle.EMPTY);
+
+        // Hook point to allow more customization.
+        runAnnotatedMethodsOnRavenwood(RavenwoodTestRunnerInitializing.class, null);
+
+        mRealRunner = instantiateRealRunner(mTestClass);
+
+        mState.enterTestRunner();
+    }
+
+    @Override
+    Runner getRealRunner() {
+        return mRealRunner;
+    }
+
+    private void runAnnotatedMethodsOnRavenwood(Class<? extends Annotation> annotationClass,
+            Object instance) {
+        Log.v(TAG, "runAnnotatedMethodsOnRavenwood() " + annotationClass.getName());
+
+        for (var method : mTestClass.getAnnotatedMethods(annotationClass)) {
+            ensureIsPublicVoidMethod(method.getMethod(), /* isStatic=*/ instance == null);
+
+            var methodDesc = method.getDeclaringClass().getName() + "."
+                    + method.getMethod().toString();
+            try {
+                method.getMethod().invoke(instance);
+            } catch (IllegalAccessException | InvocationTargetException e) {
+                throw logAndFail("Caught exception while running method " + methodDesc, e);
+            }
+        }
+    }
+
+    @Override
+    public void run(RunNotifier realNotifier) {
+        final var notifier = new RavenwoodRunNotifier(realNotifier);
+        final var description = getDescription();
+
+        if (mRealRunner instanceof ClassSkippingTestRunner) {
+            mRealRunner.run(notifier);
+            Log.i(TAG, "onClassSkipped: description=" + description);
+            RavenwoodTestStats.getInstance().onClassSkipped(description);
+            return;
+        }
+
+        Log.v(TAG, "Starting " + mTestJavaClass.getCanonicalName());
+        if (RAVENWOOD_VERBOSE_LOGGING) {
+            dumpDescription(description);
+        }
+
+        // TODO(b/365976974): handle nested classes better
+        final boolean skipRunnerHook =
+                mRealRunnerTakesRunnerBuilder && mRealRunner instanceof Suite;
+
+        sCurrentRunner.set(this);
+        try {
+            if (!skipRunnerHook) {
+                try {
+                    mState.enterTestClass();
+                } catch (Throwable th) {
+                    notifier.reportBeforeTestFailure(description, th);
+                    return;
+                }
+            }
+
+            // Delegate to the inner runner.
+            mRealRunner.run(notifier);
+        } finally {
+            sCurrentRunner.remove();
+
+            if (!skipRunnerHook) {
+                try {
+                    RavenwoodTestStats.getInstance().onClassFinished(description);
+                    mState.exitTestClass();
+                } catch (Throwable th) {
+                    notifier.reportAfterTestFailure(th);
+                }
+            }
+        }
+    }
+
+    private Statement wrapWithHooks(Statement base, Description description, Scope scope,
+            Order order) {
+        return new Statement() {
+            @Override
+            public void evaluate() throws Throwable {
+                runWithHooks(description, scope, order, base);
+            }
+        };
+    }
+
+    private void runWithHooks(Description description, Scope scope, Order order, Statement s)
+            throws Throwable {
+        assumeTrue(onBefore(description, scope, order));
+        try {
+            s.evaluate();
+            onAfter(description, scope, order, null);
+        } catch (Throwable t) {
+            if (onAfter(description, scope, order, t)) {
+                throw t;
+            }
+        }
+    }
+
+    /**
+     * A runner that simply skips a class. It still has to support {@link Filterable}
+     * because otherwise the result still says "SKIPPED" even when it's not included in the
+     * filter.
+     */
+    private static class ClassSkippingTestRunner extends Runner implements Filterable {
+        private final Description mDescription;
+        private boolean mFilteredOut;
+
+        ClassSkippingTestRunner(Class<?> testClass) {
+            mDescription = Description.createTestDescription(testClass, testClass.getSimpleName());
+            mFilteredOut = false;
+        }
+
+        @Override
+        public Description getDescription() {
+            return mDescription;
+        }
+
+        @Override
+        public void run(RunNotifier notifier) {
+            if (mFilteredOut) {
+                return;
+            }
+            notifier.fireTestSuiteStarted(mDescription);
+            notifier.fireTestIgnored(mDescription);
+            notifier.fireTestSuiteFinished(mDescription);
+        }
+
+        @Override
+        public void filter(Filter filter) throws NoTestsRemainException {
+            if (filter.shouldRun(mDescription)) {
+                mFilteredOut = false;
+            } else {
+                throw new NoTestsRemainException();
+            }
+        }
+    }
+
+    /**
+     * Called before a test / class.
+     *
+     * Return false if it should be skipped.
+     */
+    private boolean onBefore(Description description, Scope scope, Order order) {
+        Log.v(TAG, "onBefore: description=" + description + ", " + scope + ", " + order);
+
+        if (scope == Scope.Instance && order == Order.Outer) {
+            // Start of a test method.
+            mState.enterTestMethod(description);
+        }
+
+        final var classDescription = getDescription();
+
+        // Class-level annotations are checked by the runner already, so we only check
+        // method-level annotations here.
+        if (scope == Scope.Instance && order == Order.Outer) {
+            if (!RavenwoodEnablementChecker.shouldEnableOnRavenwood(description, true)) {
+                RavenwoodTestStats.getInstance().onTestFinished(
+                        classDescription, description, Result.Skipped);
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Called after a test / class.
+     *
+     * Return false if the exception should be ignored.
+     */
+    private boolean onAfter(Description description, Scope scope, Order order, Throwable th) {
+        Log.v(TAG, "onAfter: description=" + description + ", " + scope + ", " + order + ", " + th);
+
+        final var classDescription = getDescription();
+
+        if (scope == Scope.Instance && order == Order.Outer) {
+            // End of a test method.
+            mState.exitTestMethod();
+
+            final Result result;
+            if (th == null) {
+                result = Result.Passed;
+            } else if (th instanceof AssumptionViolatedException) {
+                result = Result.Skipped;
+            } else {
+                result = Result.Failed;
+            }
+
+            RavenwoodTestStats.getInstance().onTestFinished(classDescription, description, result);
+        }
+
+        // If RUN_DISABLED_TESTS is set, and the method did _not_ throw, make it an error.
+        if (RavenwoodRule.private$ravenwood().isRunningDisabledTests()
+                && scope == Scope.Instance && order == Order.Outer) {
+
+            boolean isTestEnabled = RavenwoodEnablementChecker.shouldEnableOnRavenwood(
+                    description, false);
+            if (th == null) {
+                // Test passed. Is the test method supposed to be enabled?
+                if (isTestEnabled) {
+                    // Enabled and didn't throw, okay.
+                    return true;
+                } else {
+                    // Disabled and didn't throw. We should report it.
+                    fail("Test wasn't included under Ravenwood, but it actually "
+                            + "passed under Ravenwood; consider updating annotations");
+                    return true; // unreachable.
+                }
+            } else {
+                // Test failed.
+                if (isTestEnabled) {
+                    // Enabled but failed. We should throw the exception.
+                    return true;
+                } else {
+                    // Disabled and failed. Expected. Don't throw.
+                    return false;
+                }
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Called by RavenwoodRule.
+     */
+    static void onRavenwoodRuleEnter(Description description, RavenwoodRule rule) {
+        Log.v(TAG, "onRavenwoodRuleEnter: description=" + description);
+        getCurrentRunner().mState.enterRavenwoodRule(rule);
+    }
+
+    /**
+     * Called by RavenwoodRule.
+     */
+    static void onRavenwoodRuleExit(Description description, RavenwoodRule rule) {
+        Log.v(TAG, "onRavenwoodRuleExit: description=" + description);
+        getCurrentRunner().mState.exitRavenwoodRule(rule);
+    }
+
+    private void dumpDescription(Description desc) {
+        dumpDescription(desc, "[TestDescription]=", "  ");
+    }
+
+    private void dumpDescription(Description desc, String header, String indent) {
+        Log.v(TAG, indent + header + desc);
+
+        var children = desc.getChildren();
+        var childrenIndent = "  " + indent;
+        for (int i = 0; i < children.size(); i++) {
+            dumpDescription(children.get(i), "#" + i + ": ", childrenIndent);
+        }
+    }
+
+    static volatile BiConsumer<String, Throwable> sCriticalErrorHandler = null;
+
+    static void onCriticalError(@NonNull String message, @Nullable Throwable th) {
+        Log.e(TAG, "Critical error! " + message, th);
+        var handler = sCriticalErrorHandler;
+        if (handler == null) {
+            Log.e(TAG, "Ravenwood cannot continue. Killing self process.", th);
+            System.exit(1);
+        }
+        handler.accept(message, th);
+    }
+}
diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodAwareTestRunnerHook.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodAwareTestRunnerHook.java
deleted file mode 100644
index 478bead..0000000
--- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodAwareTestRunnerHook.java
+++ /dev/null
@@ -1,199 +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 android.platform.test.ravenwood;
-
-import static org.junit.Assert.fail;
-
-import android.os.Bundle;
-import android.platform.test.ravenwood.RavenwoodAwareTestRunner.Order;
-import android.platform.test.ravenwood.RavenwoodAwareTestRunner.Scope;
-import android.platform.test.ravenwood.RavenwoodTestStats.Result;
-import android.util.Log;
-
-import androidx.test.platform.app.InstrumentationRegistry;
-
-import org.junit.runner.Description;
-import org.junit.runners.model.TestClass;
-
-/**
- * Provide hook points created by {@link RavenwoodAwareTestRunner}.
- *
- * States are associated with each {@link RavenwoodAwareTestRunner} are stored in
- * {@link RavenwoodRunnerState}, rather than as members of {@link RavenwoodAwareTestRunner}.
- * See its javadoc for the reasons.
- *
- * All methods in this class must be called from the test main thread.
- */
-public class RavenwoodAwareTestRunnerHook {
-    private static final String TAG = RavenwoodAwareTestRunner.TAG;
-
-    private RavenwoodAwareTestRunnerHook() {
-    }
-
-    /**
-     * Called before any code starts. Internally it will only initialize the environment once.
-     */
-    public static void performGlobalInitialization() {
-        RavenwoodRuntimeEnvironmentController.globalInitOnce();
-    }
-
-    /**
-     * Called when a runner starts, before the inner runner gets a chance to run.
-     */
-    public static void onRunnerInitializing(RavenwoodAwareTestRunner runner, TestClass testClass) {
-        Log.i(TAG, "onRunnerInitializing: testClass=" + testClass.getJavaClass()
-                + " runner=" + runner);
-
-        // This is needed to make AndroidJUnit4ClassRunner happy.
-        InstrumentationRegistry.registerInstance(null, Bundle.EMPTY);
-    }
-
-    /**
-     * Called when a whole test class is skipped.
-     */
-    public static void onClassSkipped(Description description) {
-        Log.i(TAG, "onClassSkipped: description=" + description);
-        RavenwoodTestStats.getInstance().onClassSkipped(description);
-    }
-
-    /**
-     * Called before the inner runner starts.
-     */
-    public static void onBeforeInnerRunnerStart(
-            RavenwoodAwareTestRunner runner, Description description) throws Throwable {
-        Log.v(TAG, "onBeforeInnerRunnerStart: description=" + description);
-
-        // Prepare the environment before the inner runner starts.
-        runner.mState.enterTestClass(description);
-    }
-
-    /**
-     * Called after the inner runner finished.
-     */
-    public static void onAfterInnerRunnerFinished(
-            RavenwoodAwareTestRunner runner, Description description) throws Throwable {
-        Log.v(TAG, "onAfterInnerRunnerFinished: description=" + description);
-
-        RavenwoodTestStats.getInstance().onClassFinished(description);
-        runner.mState.exitTestClass();
-    }
-
-    /**
-     * Called before a test / class.
-     *
-     * Return false if it should be skipped.
-     */
-    public static boolean onBefore(RavenwoodAwareTestRunner runner, Description description,
-            Scope scope, Order order) throws Throwable {
-        Log.v(TAG, "onBefore: description=" + description + ", " + scope + ", " + order);
-
-        if (scope == Scope.Instance && order == Order.Outer) {
-            // Start of a test method.
-            runner.mState.enterTestMethod(description);
-        }
-
-        final var classDescription = runner.mState.getClassDescription();
-
-        // Class-level annotations are checked by the runner already, so we only check
-        // method-level annotations here.
-        if (scope == Scope.Instance && order == Order.Outer) {
-            if (!RavenwoodEnablementChecker.shouldEnableOnRavenwood(
-                    description, true)) {
-                RavenwoodTestStats.getInstance().onTestFinished(
-                        classDescription, description, Result.Skipped);
-                return false;
-            }
-        }
-        return true;
-    }
-
-    /**
-     * Called after a test / class.
-     *
-     * Return false if the exception should be ignored.
-     */
-    public static boolean onAfter(RavenwoodAwareTestRunner runner, Description description,
-            Scope scope, Order order, Throwable th) {
-        Log.v(TAG, "onAfter: description=" + description + ", " + scope + ", " + order + ", " + th);
-
-        final var classDescription = runner.mState.getClassDescription();
-
-        if (scope == Scope.Instance && order == Order.Outer) {
-            // End of a test method.
-            runner.mState.exitTestMethod();
-            RavenwoodTestStats.getInstance().onTestFinished(classDescription, description,
-                    th == null ? Result.Passed : Result.Failed);
-        }
-
-        // If RUN_DISABLED_TESTS is set, and the method did _not_ throw, make it an error.
-        if (RavenwoodRule.private$ravenwood().isRunningDisabledTests()
-                && scope == Scope.Instance && order == Order.Outer) {
-
-            boolean isTestEnabled = RavenwoodEnablementChecker.shouldEnableOnRavenwood(
-                    description, false);
-            if (th == null) {
-                // Test passed. Is the test method supposed to be enabled?
-                if (isTestEnabled) {
-                    // Enabled and didn't throw, okay.
-                    return true;
-                } else {
-                    // Disabled and didn't throw. We should report it.
-                    fail("Test wasn't included under Ravenwood, but it actually "
-                            + "passed under Ravenwood; consider updating annotations");
-                    return true; // unreachable.
-                }
-            } else {
-                // Test failed.
-                if (isTestEnabled) {
-                    // Enabled but failed. We should throw the exception.
-                    return true;
-                } else {
-                    // Disabled and failed. Expected. Don't throw.
-                    return false;
-                }
-            }
-        }
-        return true;
-    }
-
-    /**
-     * Called by {@link RavenwoodAwareTestRunner} to see if it should run a test class or not.
-     */
-    public static boolean shouldRunClassOnRavenwood(Class<?> clazz) {
-        return RavenwoodEnablementChecker.shouldRunClassOnRavenwood(clazz, true);
-    }
-
-    /**
-     * Called by RavenwoodRule.
-     */
-    public static void onRavenwoodRuleEnter(RavenwoodAwareTestRunner runner,
-            Description description, RavenwoodRule rule) throws Throwable {
-        Log.v(TAG, "onRavenwoodRuleEnter: description=" + description);
-
-        runner.mState.enterRavenwoodRule(rule);
-    }
-
-
-    /**
-     * Called by RavenwoodRule.
-     */
-    public static void onRavenwoodRuleExit(RavenwoodAwareTestRunner runner,
-            Description description, RavenwoodRule rule) throws Throwable {
-        Log.v(TAG, "onRavenwoodRuleExit: description=" + description);
-
-        runner.mState.exitRavenwoodRule(rule);
-    }
-}
diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodConfigPrivate.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodConfigPrivate.java
new file mode 100644
index 0000000..ffb642d
--- /dev/null
+++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodConfigPrivate.java
@@ -0,0 +1,36 @@
+/*
+ * 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.annotation.Nullable;
+
+import java.util.function.BiConsumer;
+
+/**
+ * Contains Ravenwood private APIs.
+ */
+public class RavenwoodConfigPrivate {
+    private RavenwoodConfigPrivate() {
+    }
+
+    /**
+     * Set a listener for onCriticalError(), for testing. If a listener is set, we won't call
+     * System.exit().
+     */
+    public static void setCriticalErrorHandler(@Nullable BiConsumer<String, Throwable> handler) {
+        RavenwoodAwareTestRunner.sCriticalErrorHandler = handler;
+    }
+}
diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRunNotifier.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRunNotifier.java
new file mode 100644
index 0000000..6903035
--- /dev/null
+++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRunNotifier.java
@@ -0,0 +1,227 @@
+/*
+ * 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.util.Log;
+
+import org.junit.AssumptionViolatedException;
+import org.junit.runner.Description;
+import org.junit.runner.Result;
+import org.junit.runner.notification.Failure;
+import org.junit.runner.notification.RunListener;
+import org.junit.runner.notification.RunNotifier;
+import org.junit.runner.notification.StoppedByUserException;
+import org.junit.runners.model.MultipleFailureException;
+
+import java.util.ArrayList;
+import java.util.Stack;
+
+/**
+ * A run notifier that wraps another notifier and provides the following features:
+ * - Handle a failure that happened before testStarted and testEnded (typically that means
+ *   it's from @BeforeClass or @AfterClass, or a @ClassRule) and deliver it as if
+ *   individual tests in the class reported it. This is for b/364395552.
+ *
+ * - Logging.
+ */
+class RavenwoodRunNotifier extends RunNotifier {
+    private final RunNotifier mRealNotifier;
+
+    private final Stack<Description> mSuiteStack = new Stack<>();
+    private Description mCurrentSuite = null;
+    private final ArrayList<Throwable> mOutOfTestFailures = new ArrayList<>();
+
+    private boolean mBeforeTest = true;
+    private boolean mAfterTest = false;
+
+    RavenwoodRunNotifier(RunNotifier realNotifier) {
+        mRealNotifier = realNotifier;
+    }
+
+    private boolean isInTest() {
+        return !mBeforeTest && !mAfterTest;
+    }
+
+    @Override
+    public void addListener(RunListener listener) {
+        mRealNotifier.addListener(listener);
+    }
+
+    @Override
+    public void removeListener(RunListener listener) {
+        mRealNotifier.removeListener(listener);
+    }
+
+    @Override
+    public void addFirstListener(RunListener listener) {
+        mRealNotifier.addFirstListener(listener);
+    }
+
+    @Override
+    public void fireTestRunStarted(Description description) {
+        Log.i(RavenwoodAwareTestRunner.TAG, "testRunStarted: " + description);
+        mRealNotifier.fireTestRunStarted(description);
+    }
+
+    @Override
+    public void fireTestRunFinished(Result result) {
+        Log.i(RavenwoodAwareTestRunner.TAG, "testRunFinished: "
+                + result.getRunCount() + ","
+                + result.getFailureCount() + ","
+                + result.getAssumptionFailureCount() + ","
+                + result.getIgnoreCount());
+        mRealNotifier.fireTestRunFinished(result);
+    }
+
+    @Override
+    public void fireTestSuiteStarted(Description description) {
+        Log.i(RavenwoodAwareTestRunner.TAG, "testSuiteStarted: " + description);
+        mRealNotifier.fireTestSuiteStarted(description);
+
+        mBeforeTest = true;
+        mAfterTest = false;
+
+        // Keep track of the current suite, needed if the outer test is a Suite,
+        // in which case its children are test classes. (not test methods)
+        mCurrentSuite = description;
+        mSuiteStack.push(description);
+
+        mOutOfTestFailures.clear();
+    }
+
+    @Override
+    public void fireTestSuiteFinished(Description description) {
+        Log.i(RavenwoodAwareTestRunner.TAG, "testSuiteFinished: " + description);
+        mRealNotifier.fireTestSuiteFinished(description);
+
+        maybeHandleOutOfTestFailures();
+
+        mBeforeTest = true;
+        mAfterTest = false;
+
+        // Restore the upper suite.
+        mSuiteStack.pop();
+        mCurrentSuite = mSuiteStack.size() == 0 ? null : mSuiteStack.peek();
+    }
+
+    @Override
+    public void fireTestStarted(Description description) throws StoppedByUserException {
+        Log.i(RavenwoodAwareTestRunner.TAG, "testStarted: " + description);
+        mRealNotifier.fireTestStarted(description);
+
+        mAfterTest = false;
+        mBeforeTest = false;
+    }
+
+    @Override
+    public void fireTestFailure(Failure failure) {
+        Log.i(RavenwoodAwareTestRunner.TAG, "testFailure: " + failure);
+
+        if (isInTest()) {
+            mRealNotifier.fireTestFailure(failure);
+        } else {
+            mOutOfTestFailures.add(failure.getException());
+        }
+    }
+
+    @Override
+    public void fireTestAssumptionFailed(Failure failure) {
+        Log.i(RavenwoodAwareTestRunner.TAG, "testAssumptionFailed: " + failure);
+
+        if (isInTest()) {
+            mRealNotifier.fireTestAssumptionFailed(failure);
+        } else {
+            mOutOfTestFailures.add(failure.getException());
+        }
+    }
+
+    @Override
+    public void fireTestIgnored(Description description) {
+        Log.i(RavenwoodAwareTestRunner.TAG, "testIgnored: " + description);
+        mRealNotifier.fireTestIgnored(description);
+    }
+
+    @Override
+    public void fireTestFinished(Description description) {
+        Log.i(RavenwoodAwareTestRunner.TAG, "testFinished: " + description);
+        mRealNotifier.fireTestFinished(description);
+
+        mAfterTest = true;
+    }
+
+    @Override
+    public void pleaseStop() {
+        Log.w(RavenwoodAwareTestRunner.TAG, "pleaseStop:");
+        mRealNotifier.pleaseStop();
+    }
+
+    /**
+     * At the end of each Suite, we handle failures happened out of test methods.
+     * (typically in @BeforeClass or @AfterClasses)
+     *
+     * This is to work around b/364395552.
+     */
+    private boolean maybeHandleOutOfTestFailures() {
+        if (mOutOfTestFailures.size() == 0) {
+            return false;
+        }
+        Throwable th;
+        if (mOutOfTestFailures.size() == 1) {
+            th = mOutOfTestFailures.get(0);
+        } else {
+            th = new MultipleFailureException(mOutOfTestFailures);
+        }
+        if (mBeforeTest) {
+            reportBeforeTestFailure(mCurrentSuite, th);
+            return true;
+        }
+        if (mAfterTest) {
+            reportAfterTestFailure(th);
+            return true;
+        }
+        return false;
+    }
+
+    public void reportBeforeTestFailure(Description suiteDesc, Throwable th) {
+        // If a failure happens befere running any tests, we'll need to pretend
+        // as if each test in the suite reported the failure, to work around b/364395552.
+        for (var child : suiteDesc.getChildren()) {
+            if (child.isSuite()) {
+                // If the chiil is still a "parent" -- a test class or a test suite
+                // -- propagate to its children.
+                mRealNotifier.fireTestSuiteStarted(child);
+                reportBeforeTestFailure(child, th);
+                mRealNotifier.fireTestSuiteFinished(child);
+            } else {
+                mRealNotifier.fireTestStarted(child);
+                Failure f = new Failure(child, th);
+                if (th instanceof AssumptionViolatedException) {
+                    mRealNotifier.fireTestAssumptionFailed(f);
+                } else {
+                    mRealNotifier.fireTestFailure(f);
+                }
+                mRealNotifier.fireTestFinished(child);
+            }
+        }
+    }
+
+    public void reportAfterTestFailure(Throwable th) {
+        // Unfortunately, there's no good way to report it, so kill the own process.
+        RavenwoodAwareTestRunner.onCriticalError(
+                "Failures detected in @AfterClass, which would be swallowed by tradefed",
+                th);
+    }
+}
diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRunnerState.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRunnerState.java
index 03513ab..ec00e8f 100644
--- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRunnerState.java
+++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRunnerState.java
@@ -20,8 +20,8 @@
 import static org.junit.Assert.fail;
 
 import android.annotation.Nullable;
+import android.util.Log;
 
-import com.android.internal.annotations.GuardedBy;
 import com.android.ravenwood.common.RavenwoodRuntimeException;
 
 import org.junit.ClassRule;
@@ -29,9 +29,7 @@
 import org.junit.rules.TestRule;
 import org.junit.runner.Description;
 
-import java.io.IOException;
 import java.lang.reflect.Field;
-import java.util.WeakHashMap;
 
 /**
  * Used to store various states associated with the current test runner that's inly needed
@@ -45,10 +43,6 @@
 public final class RavenwoodRunnerState {
     private static final String TAG = "RavenwoodRunnerState";
 
-    @GuardedBy("sStates")
-    private static final WeakHashMap<RavenwoodAwareTestRunner, RavenwoodRunnerState> sStates =
-            new WeakHashMap<>();
-
     private final RavenwoodAwareTestRunner mRunner;
 
     /**
@@ -58,35 +52,65 @@
         mRunner = runner;
     }
 
-    private Description mClassDescription;
+    /**
+     * The RavenwoodConfig used to configure the current Ravenwood environment.
+     * This can either come from mConfig or mRule.
+     */
+    private RavenwoodConfig mCurrentConfig;
+    /**
+     * The RavenwoodConfig declared in the test class
+     */
+    private RavenwoodConfig mConfig;
+    /**
+     * The RavenwoodRule currently in effect, declared in the test class
+     */
+    private RavenwoodRule mRule;
+    private boolean mHasRavenwoodRule;
     private Description mMethodDescription;
 
-    private RavenwoodConfig mCurrentConfig;
-    private RavenwoodRule mCurrentRule;
-    private boolean mHasRavenwoodRule;
-
-    public Description getClassDescription() {
-        return mClassDescription;
+    public RavenwoodConfig getConfig() {
+        return mCurrentConfig;
     }
 
-    public void enterTestClass(Description classDescription) throws IOException {
-        mClassDescription = classDescription;
+    public void enterTestRunner() {
+        Log.i(TAG, "enterTestRunner: " + mRunner);
 
-        mHasRavenwoodRule = hasRavenwoodRule(mRunner.getTestClass().getJavaClass());
-        mCurrentConfig = extractConfiguration(mRunner.getTestClass().getJavaClass());
+        mHasRavenwoodRule = hasRavenwoodRule(mRunner.mTestJavaClass);
+        mConfig = extractConfiguration(mRunner.mTestJavaClass);
+
+        if (mConfig != null) {
+            if (mHasRavenwoodRule) {
+                fail("RavenwoodConfig and RavenwoodRule cannot be used in the same class."
+                        + " Suggest migrating to RavenwoodConfig.");
+            }
+            mCurrentConfig = mConfig;
+        } else if (!mHasRavenwoodRule) {
+            // If no RavenwoodConfig and no RavenwoodRule, use a default config
+            mCurrentConfig = new RavenwoodConfig.Builder().build();
+        }
 
         if (mCurrentConfig != null) {
-            RavenwoodRuntimeEnvironmentController.init(mCurrentConfig);
+            RavenwoodRuntimeEnvironmentController.init(mRunner);
+        }
+    }
+
+    public void enterTestClass() {
+        Log.i(TAG, "enterTestClass: " + mRunner.mTestJavaClass.getName());
+
+        if (mCurrentConfig != null) {
+            RavenwoodRuntimeEnvironmentController.init(mRunner);
         }
     }
 
     public void exitTestClass() {
-        if (mCurrentConfig != null) {
-            try {
+        Log.i(TAG, "exitTestClass: " + mRunner.mTestJavaClass.getName());
+        try {
+            if (mCurrentConfig != null) {
                 RavenwoodRuntimeEnvironmentController.reset();
-            } finally {
-                mClassDescription = null;
             }
+        } finally {
+            mConfig = null;
+            mRule = null;
         }
     }
 
@@ -96,55 +120,40 @@
 
     public void exitTestMethod() {
         mMethodDescription = null;
+        RavenwoodRuntimeEnvironmentController.reinit();
     }
 
-    public void enterRavenwoodRule(RavenwoodRule rule) throws IOException {
+    public void enterRavenwoodRule(RavenwoodRule rule) {
         if (!mHasRavenwoodRule) {
             fail("If you have a RavenwoodRule in your test, make sure the field type is"
                     + " RavenwoodRule so Ravenwood can detect it.");
         }
-        if (mCurrentConfig != null) {
-            fail("RavenwoodConfig and RavenwoodRule cannot be used in the same class."
-                    + " Suggest migrating to RavenwoodConfig.");
-        }
-        if (mCurrentRule != null) {
+        if (mRule != null) {
             fail("Multiple nesting RavenwoodRule's are detected in the same class,"
                     + " which is not supported.");
         }
-        mCurrentRule = rule;
-        RavenwoodRuntimeEnvironmentController.init(rule.getConfiguration());
+        mRule = rule;
+        if (mCurrentConfig == null) {
+            mCurrentConfig = rule.getConfiguration();
+        }
+        RavenwoodRuntimeEnvironmentController.init(mRunner);
     }
 
     public void exitRavenwoodRule(RavenwoodRule rule) {
-        if (mCurrentRule != rule) {
-            return; // This happens if the rule did _not_ take effect somehow.
+        if (mRule != rule) {
+            fail("RavenwoodRule did not take effect.");
         }
-
-        try {
-            RavenwoodRuntimeEnvironmentController.reset();
-        } finally {
-            mCurrentRule = null;
-        }
+        mRule = null;
     }
 
     /**
      * @return a configuration from a test class, if any.
      */
     @Nullable
-    private RavenwoodConfig extractConfiguration(Class<?> testClass) {
+    private static RavenwoodConfig extractConfiguration(Class<?> testClass) {
         var field = findConfigurationField(testClass);
         if (field == null) {
-            if (mHasRavenwoodRule) {
-                // Should be handled by RavenwoodRule
-                return null;
-            }
-
-            // If no RavenwoodConfig and no RavenwoodRule, return a default config
-            return new RavenwoodConfig.Builder().build();
-        }
-        if (mHasRavenwoodRule) {
-            fail("RavenwoodConfig and RavenwoodRule cannot be used in the same class."
-                    + " Suggest migrating to RavenwoodConfig.");
+            return null;
         }
 
         try {
diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java
index c2806da..9002e40 100644
--- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java
+++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java
@@ -137,7 +137,7 @@
         return res;
     }
 
-    private static RavenwoodConfig sConfig;
+    private static RavenwoodAwareTestRunner sRunner;
     private static RavenwoodSystemProperties sProps;
     private static boolean sInitialized = false;
 
@@ -171,6 +171,10 @@
         // Redirect stdout/stdin to liblog.
         RuntimeInit.redirectLogStreams();
 
+        // Touch some references early to ensure they're <clinit>'ed
+        Objects.requireNonNull(Build.TYPE);
+        Objects.requireNonNull(Build.VERSION.SDK);
+
         if (RAVENWOOD_VERBOSE_LOGGING) {
             RavenwoodCommonUtils.log(TAG, "Force enabling verbose logging");
             try {
@@ -191,12 +195,19 @@
     /**
      * Initialize the environment.
      */
-    public static void init(RavenwoodConfig config) throws IOException {
+    public static void init(RavenwoodAwareTestRunner runner) {
         if (RAVENWOOD_VERBOSE_LOGGING) {
-            Log.i(TAG, "init() called here", new RuntimeException("STACKTRACE"));
+            Log.i(TAG, "init() called here: " + runner, new RuntimeException("STACKTRACE"));
         }
+        if (sRunner == runner) {
+            return;
+        }
+        if (sRunner != null) {
+            reset();
+        }
+        sRunner = runner;
         try {
-            initInner(config);
+            initInner(runner.mState.getConfig());
         } catch (Exception th) {
             Log.e(TAG, "init() failed", th);
             reset();
@@ -205,10 +216,6 @@
     }
 
     private static void initInner(RavenwoodConfig config) throws IOException {
-        if (sConfig != null) {
-            throw new RavenwoodRuntimeException("Internal error: init() called without reset()");
-        }
-        sConfig = config;
         if (ENABLE_UNCAUGHT_EXCEPTION_DETECTION) {
             maybeThrowPendingUncaughtException(false);
             Thread.setDefaultUncaughtExceptionHandler(sUncaughtExceptionHandler);
@@ -216,7 +223,7 @@
 
         android.os.Process.init$ravenwood(config.mUid, config.mPid);
         sOriginalIdentityToken = Binder.clearCallingIdentity();
-        Binder.restoreCallingIdentity(packBinderIdentityToken(false, config.mUid, config.mPid));
+        reinit();
         setSystemProperties(config.mSystemProperties);
 
         ServiceManager.init$ravenwood();
@@ -269,9 +276,7 @@
         config.mInstContext = instContext;
         config.mTargetContext = targetContext;
 
-        final Supplier<Resources> systemResourcesLoader = () -> {
-            return config.mState.loadResources(null);
-        };
+        final Supplier<Resources> systemResourcesLoader = () -> config.mState.loadResources(null);
 
         config.mState.mSystemServerContext =
                 new RavenwoodContext(ANDROID_PACKAGE_NAME, main, systemResourcesLoader);
@@ -288,10 +293,14 @@
                     RavenwoodRuntimeEnvironmentController::dumpStacks,
                     TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
         }
+    }
 
-        // Touch some references early to ensure they're <clinit>'ed
-        Objects.requireNonNull(Build.TYPE);
-        Objects.requireNonNull(Build.VERSION.SDK);
+    /**
+     * Partially re-initialize after each test method invocation
+     */
+    public static void reinit() {
+        var config = sRunner.mState.getConfig();
+        Binder.restoreCallingIdentity(packBinderIdentityToken(false, config.mUid, config.mPid));
     }
 
     /**
@@ -301,11 +310,11 @@
         if (RAVENWOOD_VERBOSE_LOGGING) {
             Log.i(TAG, "reset() called here", new RuntimeException("STACKTRACE"));
         }
-        if (sConfig == null) {
+        if (sRunner == null) {
             throw new RavenwoodRuntimeException("Internal error: reset() already called");
         }
-        var config = sConfig;
-        sConfig = null;
+        var config = sRunner.mState.getConfig();
+        sRunner = null;
 
         if (ENABLE_TIMEOUT_STACKS) {
             sPendingTimeout.cancel(false);
diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodTestStats.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodTestStats.java
index b4b8715..016de8e 100644
--- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodTestStats.java
+++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodTestStats.java
@@ -90,9 +90,7 @@
         // Create the "latest" symlink.
         Path symlink = Paths.get(tmpdir, basename + "latest.csv");
         try {
-            if (Files.exists(symlink)) {
-                Files.delete(symlink);
-            }
+            Files.deleteIfExists(symlink);
             Files.createSymbolicLink(symlink, Paths.get(mOutputFile.getName()));
 
         } catch (IOException e) {
diff --git a/ravenwood/junit-src/android/platform/test/annotations/RavenwoodTestRunnerInitializing.java b/ravenwood/junit-src/android/platform/test/annotations/RavenwoodTestRunnerInitializing.java
new file mode 100644
index 0000000..3bba27a
--- /dev/null
+++ b/ravenwood/junit-src/android/platform/test/annotations/RavenwoodTestRunnerInitializing.java
@@ -0,0 +1,32 @@
+/*
+ * 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.annotations;
+
+import static java.lang.annotation.ElementType.METHOD;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * An annotation similar to JUnit's BeforeClass, but this gets executed before
+ * the inner runner is instantiated, and only on Ravenwood.
+ * It can be used to initialize what's needed by the inner runner.
+ */
+@Target({METHOD})
+@Retention(RetentionPolicy.RUNTIME)
+public @interface RavenwoodTestRunnerInitializing {
+}
diff --git a/ravenwood/junit-src/android/platform/test/annotations/internal/InnerRunner.java b/ravenwood/junit-src/android/platform/test/annotations/internal/InnerRunner.java
new file mode 100644
index 0000000..dde53a5
--- /dev/null
+++ b/ravenwood/junit-src/android/platform/test/annotations/internal/InnerRunner.java
@@ -0,0 +1,32 @@
+/*
+ * 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.annotations.internal;
+
+import static java.lang.annotation.ElementType.TYPE;
+
+import org.junit.runner.Runner;
+
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Inherited
+@Target({TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+public @interface InnerRunner {
+    Class<? extends Runner> value();
+}
diff --git a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodAwareTestRunner.java b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodAwareTestRunner.java
deleted file mode 100644
index 5ba972df..0000000
--- a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodAwareTestRunner.java
+++ /dev/null
@@ -1,733 +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 android.platform.test.ravenwood;
-
-import static com.android.ravenwood.common.RavenwoodCommonUtils.RAVENWOOD_VERBOSE_LOGGING;
-import static com.android.ravenwood.common.RavenwoodCommonUtils.ensureIsPublicVoidMethod;
-import static com.android.ravenwood.common.RavenwoodCommonUtils.isOnRavenwood;
-
-import static java.lang.annotation.ElementType.METHOD;
-import static java.lang.annotation.ElementType.TYPE;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.util.Log;
-
-import org.junit.Assume;
-import org.junit.AssumptionViolatedException;
-import org.junit.internal.builders.AllDefaultPossibilitiesBuilder;
-import org.junit.rules.TestRule;
-import org.junit.runner.Description;
-import org.junit.runner.Result;
-import org.junit.runner.Runner;
-import org.junit.runner.manipulation.Filter;
-import org.junit.runner.manipulation.Filterable;
-import org.junit.runner.manipulation.InvalidOrderingException;
-import org.junit.runner.manipulation.NoTestsRemainException;
-import org.junit.runner.manipulation.Orderable;
-import org.junit.runner.manipulation.Orderer;
-import org.junit.runner.manipulation.Sortable;
-import org.junit.runner.manipulation.Sorter;
-import org.junit.runner.notification.Failure;
-import org.junit.runner.notification.RunListener;
-import org.junit.runner.notification.RunNotifier;
-import org.junit.runner.notification.StoppedByUserException;
-import org.junit.runners.BlockJUnit4ClassRunner;
-import org.junit.runners.Suite;
-import org.junit.runners.model.MultipleFailureException;
-import org.junit.runners.model.RunnerBuilder;
-import org.junit.runners.model.Statement;
-import org.junit.runners.model.TestClass;
-
-import java.lang.annotation.Annotation;
-import java.lang.annotation.Inherited;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-import java.lang.reflect.InvocationTargetException;
-import java.util.ArrayList;
-import java.util.Stack;
-import java.util.function.BiConsumer;
-
-/**
- * A test runner used for Ravenwood.
- *
- * It will delegate to another runner specified with {@link InnerRunner}
- * (default = {@link BlockJUnit4ClassRunner}) with the following features.
- * - Add a {@link RavenwoodAwareTestRunnerHook#onRunnerInitializing} hook, which is called before
- *   the inner runner gets a chance to run. This can be used to initialize stuff used by the
- *   inner runner.
- * - Add hook points, which are handed by RavenwoodAwareTestRunnerHook, with help from
- *   the four test rules such as {@link #sImplicitClassOuterRule}, which are also injected by
- *   the ravenizer tool.
- *
- * We use this runner to:
- * - Initialize the bare minimum environmnet just to be enough to make the actual test runners
- *   happy.
- * - Handle {@link android.platform.test.annotations.DisabledOnRavenwood}.
- *
- * This class is built such that it can also be used on a real device, but in that case
- * it will basically just delegate to the inner wrapper, and won't do anything special.
- * (no hooks, etc.)
- */
-public final class RavenwoodAwareTestRunner extends Runner implements Filterable, Orderable {
-    public static final String TAG = "Ravenwood";
-
-    @Inherited
-    @Target({TYPE})
-    @Retention(RetentionPolicy.RUNTIME)
-    public @interface InnerRunner {
-        Class<? extends Runner> value();
-    }
-
-    /**
-     * An annotation similar to JUnit's BeforeClass, but this gets executed before
-     * the inner runner is instantiated, and only on Ravenwood.
-     * It can be used to initialize what's needed by the inner runner.
-     */
-    @Target({METHOD})
-    @Retention(RetentionPolicy.RUNTIME)
-    public @interface RavenwoodTestRunnerInitializing {
-    }
-
-    /** Scope of a hook. */
-    public enum Scope {
-        Class,
-        Instance,
-    }
-
-    /** Order of a hook. */
-    public enum Order {
-        Outer,
-        Inner,
-    }
-
-    // The following four rule instances will be injected to tests by the Ravenizer tool.
-    private static class RavenwoodClassOuterRule implements TestRule {
-        @Override
-        public Statement apply(Statement base, Description description) {
-            return getCurrentRunner().wrapWithHooks(base, description, Scope.Class, Order.Outer);
-        }
-    }
-
-    private static class RavenwoodClassInnerRule implements TestRule {
-        @Override
-        public Statement apply(Statement base, Description description) {
-            return getCurrentRunner().wrapWithHooks(base, description, Scope.Class, Order.Inner);
-        }
-    }
-
-    private static class RavenwoodInstanceOuterRule implements TestRule {
-        @Override
-        public Statement apply(Statement base, Description description) {
-            return getCurrentRunner().wrapWithHooks(
-                    base, description, Scope.Instance, Order.Outer);
-        }
-    }
-
-    private static class RavenwoodInstanceInnerRule implements TestRule {
-        @Override
-        public Statement apply(Statement base, Description description) {
-            return getCurrentRunner().wrapWithHooks(
-                    base, description, Scope.Instance, Order.Inner);
-        }
-    }
-
-    public static final TestRule sImplicitClassOuterRule = new RavenwoodClassOuterRule();
-    public static final TestRule sImplicitClassInnerRule = new RavenwoodClassInnerRule();
-    public static final TestRule sImplicitInstOuterRule = new RavenwoodInstanceOuterRule();
-    public static final TestRule sImplicitInstInnerRule = new RavenwoodInstanceOuterRule();
-
-    public static final String IMPLICIT_CLASS_OUTER_RULE_NAME = "sImplicitClassOuterRule";
-    public static final String IMPLICIT_CLASS_INNER_RULE_NAME = "sImplicitClassInnerRule";
-    public static final String IMPLICIT_INST_OUTER_RULE_NAME = "sImplicitInstOuterRule";
-    public static final String IMPLICIT_INST_INNER_RULE_NAME = "sImplicitInstInnerRule";
-
-    /** Keeps track of the runner on the current thread. */
-    private static final ThreadLocal<RavenwoodAwareTestRunner> sCurrentRunner = new ThreadLocal<>();
-
-    static RavenwoodAwareTestRunner getCurrentRunner() {
-        var runner = sCurrentRunner.get();
-        if (runner == null) {
-            throw new RuntimeException("Current test runner not set!");
-        }
-        return runner;
-    }
-
-    private final Class<?> mTestJavaClass;
-    private TestClass mTestClass = null;
-    private Runner mRealRunner = null;
-    private Description mDescription = null;
-    private Throwable mExceptionInConstructor = null;
-    private boolean mRealRunnerTakesRunnerBuilder = false;
-
-    /**
-     * Stores internal states / methods associated with this runner that's only needed in
-     * junit-impl.
-     */
-    final RavenwoodRunnerState mState = new RavenwoodRunnerState(this);
-
-    private Error logAndFail(String message, Throwable exception) {
-        Log.e(TAG, message, exception);
-        throw new AssertionError(message, exception);
-    }
-
-    public TestClass getTestClass() {
-        return mTestClass;
-    }
-
-    /**
-     * Constructor.
-     */
-    public RavenwoodAwareTestRunner(Class<?> testClass) {
-        mTestJavaClass = testClass;
-        try {
-            performGlobalInitialization();
-
-            /*
-             * If the class has @DisabledOnRavenwood, then we'll delegate to
-             * ClassSkippingTestRunner, which simply skips it.
-             *
-             * We need to do it before instantiating TestClass for b/367694651.
-             */
-            if (isOnRavenwood() && !RavenwoodAwareTestRunnerHook.shouldRunClassOnRavenwood(
-                    testClass)) {
-                mRealRunner = new ClassSkippingTestRunner(testClass);
-                mDescription = mRealRunner.getDescription();
-                return;
-            }
-
-            mTestClass = new TestClass(testClass);
-
-            Log.v(TAG, "RavenwoodAwareTestRunner starting for " + testClass.getCanonicalName());
-
-            onRunnerInitializing();
-
-            // Find the real runner.
-            final Class<? extends Runner> realRunnerClass;
-            final InnerRunner innerRunnerAnnotation = mTestClass.getAnnotation(InnerRunner.class);
-            if (innerRunnerAnnotation != null) {
-                realRunnerClass = innerRunnerAnnotation.value();
-            } else {
-                // Default runner.
-                realRunnerClass = BlockJUnit4ClassRunner.class;
-            }
-
-            try {
-                Log.i(TAG, "Initializing the inner runner: " + realRunnerClass);
-
-                mRealRunner = instantiateRealRunner(realRunnerClass, testClass);
-                mDescription = mRealRunner.getDescription();
-
-            } catch (InstantiationException | IllegalAccessException
-                     | InvocationTargetException | NoSuchMethodException e) {
-                throw logAndFail("Failed to instantiate " + realRunnerClass, e);
-            }
-        } catch (Throwable th) {
-            // If we throw in the constructor, Tradefed may not report it and just ignore the class,
-            // so record it and throw it when the test actually started.
-            Log.e(TAG, "Fatal: Exception detected in constructor", th);
-            mExceptionInConstructor = new RuntimeException("Exception detected in constructor",
-                    th);
-            mDescription = Description.createTestDescription(testClass, "Constructor");
-
-            // This is for testing if tradefed is fixed.
-            if ("1".equals(System.getenv("RAVENWOOD_THROW_EXCEPTION_IN_TEST_RUNNER"))) {
-                throw th;
-            }
-        }
-    }
-
-    private Runner instantiateRealRunner(
-            Class<? extends Runner> realRunnerClass,
-            Class<?> testClass)
-            throws NoSuchMethodException, InvocationTargetException, InstantiationException,
-            IllegalAccessException {
-        try {
-            return realRunnerClass.getConstructor(Class.class).newInstance(testClass);
-        } catch (NoSuchMethodException e) {
-            var constructor = realRunnerClass.getConstructor(Class.class, RunnerBuilder.class);
-            mRealRunnerTakesRunnerBuilder = true;
-            return constructor.newInstance(testClass, new AllDefaultPossibilitiesBuilder());
-        }
-    }
-
-    private void performGlobalInitialization() {
-        if (!isOnRavenwood()) {
-            return;
-        }
-        RavenwoodAwareTestRunnerHook.performGlobalInitialization();
-    }
-
-    /**
-     * Run the bare minimum setup to initialize the wrapped runner.
-     */
-    // This method is called by the ctor, so never make it virtual.
-    private void onRunnerInitializing() {
-        if (!isOnRavenwood()) {
-            return;
-        }
-
-        RavenwoodAwareTestRunnerHook.onRunnerInitializing(this, mTestClass);
-
-        // Hook point to allow more customization.
-        runAnnotatedMethodsOnRavenwood(RavenwoodTestRunnerInitializing.class, null);
-    }
-
-    private void runAnnotatedMethodsOnRavenwood(Class<? extends Annotation> annotationClass,
-            Object instance) {
-        if (!isOnRavenwood()) {
-            return;
-        }
-        Log.v(TAG, "runAnnotatedMethodsOnRavenwood() " + annotationClass.getName());
-
-        for (var method : getTestClass().getAnnotatedMethods(annotationClass)) {
-            ensureIsPublicVoidMethod(method.getMethod(), /* isStatic=*/ instance == null);
-
-            var methodDesc = method.getDeclaringClass().getName() + "."
-                    + method.getMethod().toString();
-            try {
-                method.getMethod().invoke(instance);
-            } catch (IllegalAccessException | InvocationTargetException e) {
-                throw logAndFail("Caught exception while running method " + methodDesc, e);
-            }
-        }
-    }
-
-    @Override
-    public Description getDescription() {
-        return mDescription;
-    }
-
-    @Override
-    public void run(RunNotifier realNotifier) {
-        final RavenwoodRunNotifier notifier = new RavenwoodRunNotifier(realNotifier);
-
-        if (mRealRunner instanceof ClassSkippingTestRunner) {
-            mRealRunner.run(notifier);
-            RavenwoodAwareTestRunnerHook.onClassSkipped(getDescription());
-            return;
-        }
-
-        Log.v(TAG, "Starting " + mTestJavaClass.getCanonicalName());
-        if (RAVENWOOD_VERBOSE_LOGGING) {
-            dumpDescription(getDescription());
-        }
-
-        if (maybeReportExceptionFromConstructor(notifier)) {
-            return;
-        }
-
-        // TODO(b/365976974): handle nested classes better
-        final boolean skipRunnerHook =
-                mRealRunnerTakesRunnerBuilder && mRealRunner instanceof Suite;
-
-        sCurrentRunner.set(this);
-        try {
-            if (!skipRunnerHook) {
-                try {
-                    RavenwoodAwareTestRunnerHook.onBeforeInnerRunnerStart(
-                            this, getDescription());
-                } catch (Throwable th) {
-                    notifier.reportBeforeTestFailure(getDescription(), th);
-                    return;
-                }
-            }
-
-            // Delegate to the inner runner.
-            mRealRunner.run(notifier);
-        } finally {
-            sCurrentRunner.remove();
-
-            if (!skipRunnerHook) {
-                try {
-                    RavenwoodAwareTestRunnerHook.onAfterInnerRunnerFinished(
-                            this, getDescription());
-                } catch (Throwable th) {
-                    notifier.reportAfterTestFailure(th);
-                }
-            }
-        }
-    }
-
-    /** Throw the exception detected in the constructor, if any. */
-    private boolean maybeReportExceptionFromConstructor(RunNotifier notifier) {
-        if (mExceptionInConstructor == null) {
-            return false;
-        }
-        notifier.fireTestStarted(mDescription);
-        notifier.fireTestFailure(new Failure(mDescription, mExceptionInConstructor));
-        notifier.fireTestFinished(mDescription);
-
-        return true;
-    }
-
-    @Override
-    public void filter(Filter filter) throws NoTestsRemainException {
-        if (mRealRunner instanceof Filterable r) {
-            r.filter(filter);
-        }
-    }
-
-    @Override
-    public void order(Orderer orderer) throws InvalidOrderingException {
-        if (mRealRunner instanceof Orderable r) {
-            r.order(orderer);
-        }
-    }
-
-    @Override
-    public void sort(Sorter sorter) {
-        if (mRealRunner instanceof Sortable r) {
-            r.sort(sorter);
-        }
-    }
-
-    private Statement wrapWithHooks(Statement base, Description description, Scope scope,
-            Order order) {
-        if (!isOnRavenwood()) {
-            return base;
-        }
-        return new Statement() {
-            @Override
-            public void evaluate() throws Throwable {
-                runWithHooks(description, scope, order, base);
-            }
-        };
-    }
-
-    private void runWithHooks(Description description, Scope scope, Order order, Runnable r)
-            throws Throwable {
-        runWithHooks(description, scope, order, new Statement() {
-            @Override
-            public void evaluate() throws Throwable {
-                r.run();
-            }
-        });
-    }
-
-    private void runWithHooks(Description description, Scope scope, Order order, Statement s)
-            throws Throwable {
-        if (isOnRavenwood()) {
-            Assume.assumeTrue(
-                    RavenwoodAwareTestRunnerHook.onBefore(this, description, scope, order));
-        }
-        try {
-            s.evaluate();
-            if (isOnRavenwood()) {
-                RavenwoodAwareTestRunnerHook.onAfter(this, description, scope, order, null);
-            }
-        } catch (Throwable t) {
-            boolean shouldThrow = true;
-            if (isOnRavenwood()) {
-                shouldThrow = RavenwoodAwareTestRunnerHook.onAfter(
-                        this, description, scope, order, t);
-            }
-            if (shouldThrow) {
-                throw t;
-            }
-        }
-    }
-
-    /**
-     * A runner that simply skips a class. It still has to support {@link Filterable}
-     * because otherwise the result still says "SKIPPED" even when it's not included in the
-     * filter.
-     */
-    private static class ClassSkippingTestRunner extends Runner implements Filterable {
-        private final Description mDescription;
-        private boolean mFilteredOut;
-
-        ClassSkippingTestRunner(Class<?> testClass) {
-            mDescription = Description.createTestDescription(testClass, testClass.getSimpleName());
-            mFilteredOut = false;
-        }
-
-        @Override
-        public Description getDescription() {
-            return mDescription;
-        }
-
-        @Override
-        public void run(RunNotifier notifier) {
-            if (mFilteredOut) {
-                return;
-            }
-            notifier.fireTestSuiteStarted(mDescription);
-            notifier.fireTestIgnored(mDescription);
-            notifier.fireTestSuiteFinished(mDescription);
-        }
-
-        @Override
-        public void filter(Filter filter) throws NoTestsRemainException {
-            if (filter.shouldRun(mDescription)) {
-                mFilteredOut = false;
-            } else {
-                throw new NoTestsRemainException();
-            }
-        }
-    }
-
-    private void dumpDescription(Description desc) {
-        dumpDescription(desc, "[TestDescription]=", "  ");
-    }
-
-    private void dumpDescription(Description desc, String header, String indent) {
-        Log.v(TAG, indent + header + desc);
-
-        var children = desc.getChildren();
-        var childrenIndent = "  " + indent;
-        for (int i = 0; i < children.size(); i++) {
-            dumpDescription(children.get(i), "#" + i + ": ", childrenIndent);
-        }
-    }
-
-    /**
-     * A run notifier that wraps another notifier and provides the following features:
-     * - Handle a failure that happened before testStarted and testEnded (typically that means
-     *   it's from @BeforeClass or @AfterClass, or a @ClassRule) and deliver it as if
-     *   individual tests in the class reported it. This is for b/364395552.
-     *
-     * - Logging.
-     */
-    private class RavenwoodRunNotifier extends RunNotifier {
-        private final RunNotifier mRealNotifier;
-
-        private final Stack<Description> mSuiteStack = new Stack<>();
-        private Description mCurrentSuite = null;
-        private final ArrayList<Throwable> mOutOfTestFailures = new ArrayList<>();
-
-        private boolean mBeforeTest = true;
-        private boolean mAfterTest = false;
-
-        private RavenwoodRunNotifier(RunNotifier realNotifier) {
-            mRealNotifier = realNotifier;
-        }
-
-        private boolean isInTest() {
-            return !mBeforeTest && !mAfterTest;
-        }
-
-        @Override
-        public void addListener(RunListener listener) {
-            mRealNotifier.addListener(listener);
-        }
-
-        @Override
-        public void removeListener(RunListener listener) {
-            mRealNotifier.removeListener(listener);
-        }
-
-        @Override
-        public void addFirstListener(RunListener listener) {
-            mRealNotifier.addFirstListener(listener);
-        }
-
-        @Override
-        public void fireTestRunStarted(Description description) {
-            Log.i(TAG, "testRunStarted: " + description);
-            mRealNotifier.fireTestRunStarted(description);
-        }
-
-        @Override
-        public void fireTestRunFinished(Result result) {
-            Log.i(TAG, "testRunFinished: "
-                    + result.getRunCount() + ","
-                    + result.getFailureCount() + ","
-                    + result.getAssumptionFailureCount() + ","
-                    + result.getIgnoreCount());
-            mRealNotifier.fireTestRunFinished(result);
-        }
-
-        @Override
-        public void fireTestSuiteStarted(Description description) {
-            Log.i(TAG, "testSuiteStarted: " + description);
-            mRealNotifier.fireTestSuiteStarted(description);
-
-            mBeforeTest = true;
-            mAfterTest = false;
-
-            // Keep track of the current suite, needed if the outer test is a Suite,
-            // in which case its children are test classes. (not test methods)
-            mCurrentSuite = description;
-            mSuiteStack.push(description);
-
-            mOutOfTestFailures.clear();
-        }
-
-        @Override
-        public void fireTestSuiteFinished(Description description) {
-            Log.i(TAG, "testSuiteFinished: " + description);
-            mRealNotifier.fireTestSuiteFinished(description);
-
-            maybeHandleOutOfTestFailures();
-
-            mBeforeTest = true;
-            mAfterTest = false;
-
-            // Restore the upper suite.
-            mSuiteStack.pop();
-            mCurrentSuite = mSuiteStack.size() == 0 ? null : mSuiteStack.peek();
-        }
-
-        @Override
-        public void fireTestStarted(Description description) throws StoppedByUserException {
-            Log.i(TAG, "testStarted: " + description);
-            mRealNotifier.fireTestStarted(description);
-
-            mAfterTest = false;
-            mBeforeTest = false;
-        }
-
-        @Override
-        public void fireTestFailure(Failure failure) {
-            Log.i(TAG, "testFailure: " + failure);
-
-            if (isInTest()) {
-                mRealNotifier.fireTestFailure(failure);
-            } else {
-                mOutOfTestFailures.add(failure.getException());
-            }
-        }
-
-        @Override
-        public void fireTestAssumptionFailed(Failure failure) {
-            Log.i(TAG, "testAssumptionFailed: " + failure);
-
-            if (isInTest()) {
-                mRealNotifier.fireTestAssumptionFailed(failure);
-            } else {
-                mOutOfTestFailures.add(failure.getException());
-            }
-        }
-
-        @Override
-        public void fireTestIgnored(Description description) {
-            Log.i(TAG, "testIgnored: " + description);
-            mRealNotifier.fireTestIgnored(description);
-        }
-
-        @Override
-        public void fireTestFinished(Description description) {
-            Log.i(TAG, "testFinished: " + description);
-            mRealNotifier.fireTestFinished(description);
-
-            mAfterTest = true;
-        }
-
-        @Override
-        public void pleaseStop() {
-            Log.w(TAG, "pleaseStop:");
-            mRealNotifier.pleaseStop();
-        }
-
-        /**
-         * At the end of each Suite, we handle failures happened out of test methods.
-         * (typically in @BeforeClass or @AfterClasses)
-         *
-         * This is to work around b/364395552.
-         */
-        private boolean maybeHandleOutOfTestFailures() {
-            if (mOutOfTestFailures.size() == 0) {
-                return false;
-            }
-            Throwable th;
-            if (mOutOfTestFailures.size() == 1) {
-                th = mOutOfTestFailures.get(0);
-            } else {
-                th = new MultipleFailureException(mOutOfTestFailures);
-            }
-            if (mBeforeTest) {
-                reportBeforeTestFailure(mCurrentSuite, th);
-                return true;
-            }
-            if (mAfterTest) {
-                reportAfterTestFailure(th);
-                return true;
-            }
-            return false;
-        }
-
-        public void reportBeforeTestFailure(Description suiteDesc, Throwable th) {
-            // If a failure happens befere running any tests, we'll need to pretend
-            // as if each test in the suite reported the failure, to work around b/364395552.
-            for (var child : suiteDesc.getChildren()) {
-                if (child.isSuite()) {
-                    // If the chiil is still a "parent" -- a test class or a test suite
-                    // -- propagate to its children.
-                    mRealNotifier.fireTestSuiteStarted(child);
-                    reportBeforeTestFailure(child, th);
-                    mRealNotifier.fireTestSuiteFinished(child);
-                } else {
-                    mRealNotifier.fireTestStarted(child);
-                    Failure f = new Failure(child, th);
-                    if (th instanceof AssumptionViolatedException) {
-                        mRealNotifier.fireTestAssumptionFailed(f);
-                    } else {
-                        mRealNotifier.fireTestFailure(f);
-                    }
-                    mRealNotifier.fireTestFinished(child);
-                }
-            }
-        }
-
-        public void reportAfterTestFailure(Throwable th) {
-            // Unfortunately, there's no good way to report it, so kill the own process.
-            onCriticalError(
-                    "Failures detected in @AfterClass, which would be swallowed by tradefed",
-                    th);
-        }
-    }
-
-    private static volatile BiConsumer<String, Throwable> sCriticalErrorHanler;
-
-    private void onCriticalError(@NonNull String message, @Nullable Throwable th) {
-        Log.e(TAG, "Critical error! " + message, th);
-        var handler = sCriticalErrorHanler;
-        if (handler == null) {
-            handler = sDefaultCriticalErrorHandler;
-        }
-        handler.accept(message, th);
-    }
-
-    private static BiConsumer<String, Throwable> sDefaultCriticalErrorHandler = (message, th) -> {
-        Log.e(TAG, "Ravenwood cannot continue. Killing self process.", th);
-        System.exit(1);
-    };
-
-    /**
-     * Contains Ravenwood private APIs.
-     */
-    public static class RavenwoodPrivate {
-        private RavenwoodPrivate() {
-        }
-
-        /**
-         * Set a listener for onCriticalError(), for testing. If a listener is set, we won't call
-         * System.exit().
-         */
-        public void setCriticalErrorHandler(
-                @Nullable BiConsumer<String, Throwable> handler) {
-            sCriticalErrorHanler = handler;
-        }
-    }
-
-    private static final RavenwoodPrivate sRavenwoodPrivate = new RavenwoodPrivate();
-
-    public static RavenwoodPrivate private$ravenwood() {
-        return sRavenwoodPrivate;
-    }
-}
diff --git a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodAwareTestRunnerBase.java b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodAwareTestRunnerBase.java
new file mode 100644
index 0000000..31a1416
--- /dev/null
+++ b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodAwareTestRunnerBase.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 android.platform.test.ravenwood;
+
+import android.platform.test.annotations.internal.InnerRunner;
+import android.util.Log;
+
+import org.junit.internal.builders.AllDefaultPossibilitiesBuilder;
+import org.junit.runner.Description;
+import org.junit.runner.Runner;
+import org.junit.runner.manipulation.Filter;
+import org.junit.runner.manipulation.Filterable;
+import org.junit.runner.manipulation.InvalidOrderingException;
+import org.junit.runner.manipulation.NoTestsRemainException;
+import org.junit.runner.manipulation.Orderable;
+import org.junit.runner.manipulation.Orderer;
+import org.junit.runner.manipulation.Sortable;
+import org.junit.runner.manipulation.Sorter;
+import org.junit.runner.notification.RunNotifier;
+import org.junit.runners.BlockJUnit4ClassRunner;
+import org.junit.runners.model.RunnerBuilder;
+import org.junit.runners.model.TestClass;
+
+abstract class RavenwoodAwareTestRunnerBase extends Runner implements Filterable, Orderable {
+    private static final String TAG = "Ravenwood";
+
+    boolean mRealRunnerTakesRunnerBuilder = false;
+
+    abstract Runner getRealRunner();
+
+    final Runner instantiateRealRunner(TestClass testClass) {
+        // Find the real runner.
+        final Class<? extends Runner> runnerClass;
+        final InnerRunner innerRunnerAnnotation = testClass.getAnnotation(InnerRunner.class);
+        if (innerRunnerAnnotation != null) {
+            runnerClass = innerRunnerAnnotation.value();
+        } else {
+            // Default runner.
+            runnerClass = BlockJUnit4ClassRunner.class;
+        }
+
+        try {
+            Log.i(TAG, "Initializing the inner runner: " + runnerClass);
+            try {
+                return runnerClass.getConstructor(Class.class)
+                        .newInstance(testClass.getJavaClass());
+            } catch (NoSuchMethodException e) {
+                var constructor = runnerClass.getConstructor(Class.class, RunnerBuilder.class);
+                mRealRunnerTakesRunnerBuilder = true;
+                return constructor.newInstance(
+                        testClass.getJavaClass(), new AllDefaultPossibilitiesBuilder());
+            }
+        } catch (ReflectiveOperationException e) {
+            throw logAndFail("Failed to instantiate " + runnerClass, e);
+        }
+    }
+
+    final Error logAndFail(String message, Throwable exception) {
+        Log.e(TAG, message, exception);
+        return new AssertionError(message, exception);
+    }
+
+    @Override
+    public final Description getDescription() {
+        return getRealRunner().getDescription();
+    }
+
+    @Override
+    public final void filter(Filter filter) throws NoTestsRemainException {
+        if (getRealRunner() instanceof Filterable r) {
+            r.filter(filter);
+        }
+    }
+
+    @Override
+    public final void order(Orderer orderer) throws InvalidOrderingException {
+        if (getRealRunner() instanceof Orderable r) {
+            r.order(orderer);
+        }
+    }
+
+    @Override
+    public final void sort(Sorter sorter) {
+        if (getRealRunner() instanceof Sortable r) {
+            r.sort(sorter);
+        }
+    }
+}
diff --git a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java
index 93a6806..3d6ac0f 100644
--- a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java
+++ b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java
@@ -24,6 +24,8 @@
 import android.content.Context;
 import android.platform.test.annotations.DisabledOnRavenwood;
 
+import androidx.test.platform.app.InstrumentationRegistry;
+
 import com.android.ravenwood.common.RavenwoodCommonUtils;
 
 import org.junit.rules.TestRule;
@@ -219,8 +221,7 @@
      */
     @Deprecated
     public Context getContext() {
-        return Objects.requireNonNull(mConfiguration.mInstContext,
-                "Context is only available during @Test execution");
+        return InstrumentationRegistry.getInstrumentation().getContext();
     }
 
     /**
@@ -230,8 +231,7 @@
      */
     @Deprecated
     public Instrumentation getInstrumentation() {
-        return Objects.requireNonNull(mConfiguration.mInstrumentation,
-                "Instrumentation is only available during @Test execution");
+        return InstrumentationRegistry.getInstrumentation();
     }
 
     @Override
@@ -242,15 +242,11 @@
         return new Statement() {
             @Override
             public void evaluate() throws Throwable {
-                RavenwoodAwareTestRunnerHook.onRavenwoodRuleEnter(
-                        RavenwoodAwareTestRunner.getCurrentRunner(), description,
-                        RavenwoodRule.this);
+                RavenwoodAwareTestRunner.onRavenwoodRuleEnter(description, RavenwoodRule.this);
                 try {
                     base.evaluate();
                 } finally {
-                    RavenwoodAwareTestRunnerHook.onRavenwoodRuleExit(
-                            RavenwoodAwareTestRunner.getCurrentRunner(), description,
-                            RavenwoodRule.this);
+                    RavenwoodAwareTestRunner.onRavenwoodRuleExit(description, RavenwoodRule.this);
                 }
             }
         };
diff --git a/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodAwareTestRunner.java b/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodAwareTestRunner.java
new file mode 100644
index 0000000..b4b75178
--- /dev/null
+++ b/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodAwareTestRunner.java
@@ -0,0 +1,70 @@
+/*
+ * 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.util.Log;
+
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
+import org.junit.runner.Runner;
+import org.junit.runner.notification.RunNotifier;
+import org.junit.runners.model.Statement;
+import org.junit.runners.model.TestClass;
+
+/**
+ * A simple pass-through runner that just delegates to the inner runner without doing
+ * anything special (no hooks, etc.).
+ *
+ * This is only used when a real device-side test has Ravenizer enabled.
+ */
+public class RavenwoodAwareTestRunner extends RavenwoodAwareTestRunnerBase {
+    private static final String TAG = "Ravenwood";
+
+    private static class NopRule implements TestRule {
+        @Override
+        public Statement apply(Statement base, Description description) {
+            return base;
+        }
+    }
+
+    public static final TestRule sImplicitClassOuterRule = new NopRule();
+    public static final TestRule sImplicitClassInnerRule = sImplicitClassOuterRule;
+    public static final TestRule sImplicitInstOuterRule = sImplicitClassOuterRule;
+    public static final TestRule sImplicitInstInnerRule = sImplicitClassOuterRule;
+
+    private final Runner mRealRunner;
+
+    public RavenwoodAwareTestRunner(Class<?> clazz) {
+        Log.v(TAG, "RavenwoodAwareTestRunner starting for " + clazz.getCanonicalName());
+        mRealRunner = instantiateRealRunner(new TestClass(clazz));
+    }
+
+    @Override
+    Runner getRealRunner() {
+        return mRealRunner;
+    }
+
+    @Override
+    public void run(RunNotifier notifier) {
+        mRealRunner.run(notifier);
+    }
+
+    static void onRavenwoodRuleEnter(Description description, RavenwoodRule rule) {
+    }
+
+    static void onRavenwoodRuleExit(Description description, RavenwoodRule rule) {
+    }
+}
diff --git a/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodAwareTestRunnerHook.java b/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodAwareTestRunnerHook.java
deleted file mode 100644
index aa8c299..0000000
--- a/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodAwareTestRunnerHook.java
+++ /dev/null
@@ -1,101 +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 android.platform.test.ravenwood;
-
-import android.platform.test.ravenwood.RavenwoodAwareTestRunner.Order;
-import android.platform.test.ravenwood.RavenwoodAwareTestRunner.Scope;
-
-import org.junit.runner.Description;
-import org.junit.runners.model.TestClass;
-
-/**
- * Provide hook points created by {@link RavenwoodAwareTestRunner}. This is a version
- * that's used on a device side test.
- *
- * All methods are no-op in real device tests.
- *
- * TODO: Use some kind of factory to provide different implementation for the device test
- * and the ravenwood test.
- */
-public class RavenwoodAwareTestRunnerHook {
-    private RavenwoodAwareTestRunnerHook() {
-    }
-
-    /**
-     * Called before any code starts. Internally it will only initialize the environment once.
-     */
-    public static void performGlobalInitialization() {
-    }
-
-    /**
-     * Called when a runner starts, before the inner runner gets a chance to run.
-     */
-    public static void onRunnerInitializing(RavenwoodAwareTestRunner runner, TestClass testClass) {
-    }
-
-    /**
-     * Called when a whole test class is skipped.
-     */
-    public static void onClassSkipped(Description description) {
-    }
-
-    /**
-     * Called before the inner runner starts.
-     */
-    public static void onBeforeInnerRunnerStart(
-            RavenwoodAwareTestRunner runner, Description description) throws Throwable {
-    }
-
-    /**
-     * Called after the inner runner finished.
-     */
-    public static void onAfterInnerRunnerFinished(
-            RavenwoodAwareTestRunner runner, Description description) throws Throwable {
-    }
-
-    /**
-     * Called before a test / class.
-     *
-     * Return false if it should be skipped.
-     */
-    public static boolean onBefore(RavenwoodAwareTestRunner runner, Description description,
-            Scope scope, Order order) throws Throwable {
-        return true;
-    }
-
-    public static void onRavenwoodRuleEnter(RavenwoodAwareTestRunner runner,
-            Description description, RavenwoodRule rule) throws Throwable {
-    }
-
-    public static void onRavenwoodRuleExit(RavenwoodAwareTestRunner runner,
-            Description description, RavenwoodRule rule) throws Throwable {
-    }
-
-
-    /**
-     * Called after a test / class.
-     *
-     * Return false if the exception should be ignored.
-     */
-    public static boolean onAfter(RavenwoodAwareTestRunner runner, Description description,
-            Scope scope, Order order, Throwable th) {
-        return true;
-    }
-
-    public static boolean shouldRunClassOnRavenwood(Class<?> clazz) {
-        return true;
-    }
-}
diff --git a/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodConfigState.java b/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodConfigState.java
index 43a28ba..7d3d8b9 100644
--- a/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodConfigState.java
+++ b/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodConfigState.java
@@ -15,7 +15,7 @@
  */
 package android.platform.test.ravenwood;
 
-/** Stub class. The actual implementaetion is in junit-impl-src. */
+/** Stub class. The actual implementation is in junit-impl-src. */
 public class RavenwoodConfigState {
     public RavenwoodConfigState(RavenwoodConfig config) {
     }
diff --git a/ravenwood/scripts/ravenwood-test-summary b/ravenwood/scripts/ravenwood-test-summary
new file mode 100755
index 0000000..602fbff
--- /dev/null
+++ b/ravenwood/scripts/ravenwood-test-summary
@@ -0,0 +1,75 @@
+#!/usr/bin/env python3
+#
+# 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.
+
+'''
+Print the latest Ravenwood test execution summary
+
+Usage: /ravenwood-test-summary
+
+Example output:
+Module                                                         Passed   Failed  Skipped
+android.test.mock.ravenwood.tests                                   2        0        0
+CarLibHostUnitTest                                                565        0        7
+CarServiceHostUnitTest                                            364        0        0
+CtsAccountManagerTestCasesRavenwood                                 4        0        0
+CtsAppTestCasesRavenwood                                           21        0        0
+
+Description:
+This script finds all the test execution result from /tmp/Ravenwood-stats*,
+and shows per-module summary.
+'''
+
+import csv
+import glob
+import sys
+
+# Find the latest stats files.
+stats_files = glob.glob('/tmp/Ravenwood-stats_*_latest.csv')
+
+if len(stats_files) == 0:
+    print("No log files found.", file=sys.stderr)
+    exit(1)
+
+
+def parse_stats(file, result):
+    module = '(unknwon)'
+    passed = 0
+    failed = 0
+    skipped = 0
+    with open(file) as csvfile:
+        reader = csv.reader(csvfile, delimiter=',')
+
+
+        for i, row in enumerate(reader):
+            if i == 0: continue # Skip header line
+            module = row[0]
+            passed += int(row[3])
+            failed += int(row[4])
+            skipped += int(row[5])
+
+    result[module] = (passed, failed, skipped)
+
+
+result = {}
+
+for file in stats_files:
+    parse_stats(file, result)
+
+print('%-60s %8s %8s %8s' % ("Module", "Passed", "Failed", "Skipped"))
+
+for module in sorted(result.keys(), key=str.casefold):
+    r = result[module]
+    print('%-60s %8d %8d %8d' % (module, r[0], r[1], r[2]))
diff --git a/ravenwood/tests/bivalenttest/Android.bp b/ravenwood/tests/bivalenttest/Android.bp
index d7f4b3e..ce0033d 100644
--- a/ravenwood/tests/bivalenttest/Android.bp
+++ b/ravenwood/tests/bivalenttest/Android.bp
@@ -31,9 +31,8 @@
     ],
 }
 
-android_ravenwood_test {
-    name: "RavenwoodBivalentTest",
-
+java_defaults {
+    name: "ravenwood-bivalent-defaults",
     static_libs: [
         "androidx.annotation_annotation",
         "androidx.test.ext.junit",
@@ -51,15 +50,11 @@
     jni_libs: [
         "libravenwoodbivalenttest_jni",
     ],
-    auto_gen_config: true,
 }
 
-android_test {
-    name: "RavenwoodBivalentTest_device",
-
-    srcs: [
-        "test/**/*.java",
-    ],
+java_defaults {
+    name: "ravenwood-bivalent-device-defaults",
+    defaults: ["ravenwood-bivalent-defaults"],
     // TODO(b/371215487): migrate bivalenttest.ravenizer tests to another architecture
     exclude_srcs: [
         "test/**/ravenizer/*.java",
@@ -67,23 +62,32 @@
     static_libs: [
         "junit",
         "truth",
-
-        "androidx.annotation_annotation",
-        "androidx.test.ext.junit",
-        "androidx.test.rules",
-
-        "junit-params",
-        "platform-parametric-runner-lib",
-
         "ravenwood-junit",
     ],
-    jni_libs: [
-        "libravenwoodbivalenttest_jni",
-    ],
     test_suites: [
         "device-tests",
     ],
     optimize: {
         enabled: false,
     },
+    test_config_template: "AndroidTestTemplate.xml",
+}
+
+android_ravenwood_test {
+    name: "RavenwoodBivalentTest",
+    defaults: ["ravenwood-bivalent-defaults"],
+    auto_gen_config: true,
+}
+
+android_test {
+    name: "RavenwoodBivalentTest_device",
+    defaults: ["ravenwood-bivalent-device-defaults"],
+}
+
+android_test {
+    name: "RavenwoodBivalentTest_device_ravenizer",
+    defaults: ["ravenwood-bivalent-device-defaults"],
+    ravenizer: {
+        enabled: true,
+    },
 }
diff --git a/ravenwood/tests/bivalenttest/AndroidTest.xml b/ravenwood/tests/bivalenttest/AndroidTestTemplate.xml
similarity index 94%
rename from ravenwood/tests/bivalenttest/AndroidTest.xml
rename to ravenwood/tests/bivalenttest/AndroidTestTemplate.xml
index 9e5dd11..8f1a92c 100644
--- a/ravenwood/tests/bivalenttest/AndroidTest.xml
+++ b/ravenwood/tests/bivalenttest/AndroidTestTemplate.xml
@@ -19,7 +19,7 @@
 
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
-        <option name="test-file-name" value="RavenwoodBivalentTest_device.apk" />
+        <option name="test-file-name" value="{MODULE}.apk"/>
     </target_preparer>
 
     <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
diff --git a/ravenwood/tests/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/ravenizer/RavenwoodAwareTestRunnerTest.java b/ravenwood/tests/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/ravenizer/RavenwoodAwareTestRunnerTest.java
index d7c2c6c..4e21f86 100644
--- a/ravenwood/tests/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/ravenizer/RavenwoodAwareTestRunnerTest.java
+++ b/ravenwood/tests/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/ravenizer/RavenwoodAwareTestRunnerTest.java
@@ -16,12 +16,15 @@
 package com.android.ravenwoodtest.bivalenttest.ravenizer;
 
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
 
 import android.platform.test.annotations.DisabledOnRavenwood;
-import android.platform.test.ravenwood.RavenwoodAwareTestRunner.RavenwoodTestRunnerInitializing;
+import android.platform.test.annotations.RavenwoodTestRunnerInitializing;
 import android.platform.test.ravenwood.RavenwoodRule;
 import android.util.Log;
 
+import androidx.test.platform.app.InstrumentationRegistry;
+
 import junitparams.JUnitParamsRunner;
 import junitparams.Parameters;
 
@@ -30,6 +33,8 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.util.List;
+
 /**
  * Make sure RavenwoodAwareTestRunnerTest properly delegates to the original runner,
  * and also run the special annotated methods.
@@ -61,13 +66,22 @@
         sCallTracker.incrementMethodCallCount();
     }
 
+    public RavenwoodAwareTestRunnerTest() {
+        // Make sure the environment is already initialized when the constructor is called
+        assertNotNull(InstrumentationRegistry.getInstrumentation());
+    }
+
     @Test
     public void test1() {
         sCallTracker.incrementMethodCallCount();
     }
 
+    public static List<String> testParams() {
+        return List.of("foo", "bar");
+    }
+
     @Test
-    @Parameters({"foo", "bar"})
+    @Parameters(method = "testParams")
     public void testWithParams(String arg) {
         sCallTracker.incrementMethodCallCount();
     }
diff --git a/ravenwood/tests/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/ravenizer/RavenwoodNoRavenizerTest.java b/ravenwood/tests/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/ravenizer/RavenwoodNoRavenizerTest.java
index 9d878f4..77a807d 100644
--- a/ravenwood/tests/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/ravenizer/RavenwoodNoRavenizerTest.java
+++ b/ravenwood/tests/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/ravenizer/RavenwoodNoRavenizerTest.java
@@ -16,7 +16,7 @@
 package com.android.ravenwoodtest.bivalenttest.ravenizer;
 
 import android.platform.test.annotations.NoRavenizer;
-import android.platform.test.ravenwood.RavenwoodAwareTestRunner.RavenwoodTestRunnerInitializing;
+import android.platform.test.annotations.RavenwoodTestRunnerInitializing;
 
 import org.junit.Test;
 
diff --git a/ravenwood/tests/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/ravenizer/RavenwoodRunDisabledTestsReallyDisabledTest.java b/ravenwood/tests/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/ravenizer/RavenwoodRunDisabledTestsReallyDisabledTest.java
index c77841b..e6e617b 100644
--- a/ravenwood/tests/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/ravenizer/RavenwoodRunDisabledTestsReallyDisabledTest.java
+++ b/ravenwood/tests/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/ravenizer/RavenwoodRunDisabledTestsReallyDisabledTest.java
@@ -18,7 +18,7 @@
 import static org.junit.Assert.fail;
 
 import android.platform.test.annotations.DisabledOnRavenwood;
-import android.platform.test.ravenwood.RavenwoodAwareTestRunner.RavenwoodTestRunnerInitializing;
+import android.platform.test.annotations.RavenwoodTestRunnerInitializing;
 import android.platform.test.ravenwood.RavenwoodRule;
 import android.util.Log;
 
diff --git a/ravenwood/tests/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/ravenizer/RavenwoodRunDisabledTestsTest.java b/ravenwood/tests/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/ravenizer/RavenwoodRunDisabledTestsTest.java
index ea1a29d..ef18c82 100644
--- a/ravenwood/tests/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/ravenizer/RavenwoodRunDisabledTestsTest.java
+++ b/ravenwood/tests/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/ravenizer/RavenwoodRunDisabledTestsTest.java
@@ -18,7 +18,7 @@
 import static org.junit.Assert.fail;
 
 import android.platform.test.annotations.DisabledOnRavenwood;
-import android.platform.test.ravenwood.RavenwoodAwareTestRunner.RavenwoodTestRunnerInitializing;
+import android.platform.test.annotations.RavenwoodTestRunnerInitializing;
 import android.platform.test.ravenwood.RavenwoodRule;
 import android.util.Log;
 
diff --git a/ravenwood/tests/coretest/Android.bp b/ravenwood/tests/coretest/Android.bp
index 412744e..9dd7cc6 100644
--- a/ravenwood/tests/coretest/Android.bp
+++ b/ravenwood/tests/coretest/Android.bp
@@ -16,11 +16,14 @@
         "androidx.test.rules",
         "junit-params",
         "platform-parametric-runner-lib",
-        "truth",
 
         // This library should be removed by Ravenizer
         "mockito-target-minus-junit4",
     ],
+    libs: [
+        // We access internal private classes
+        "ravenwood-junit-impl",
+    ],
     srcs: [
         "test/**/*.java",
         "test/**/*.kt",
diff --git a/ravenwood/tests/coretest/test/com/android/ravenwoodtest/runnercallbacktests/RavenwoodRunnerCallbackTest.java b/ravenwood/tests/coretest/test/com/android/ravenwoodtest/runnercallbacktests/RavenwoodRunnerCallbackTest.java
index bd01313..6720e45 100644
--- a/ravenwood/tests/coretest/test/com/android/ravenwoodtest/runnercallbacktests/RavenwoodRunnerCallbackTest.java
+++ b/ravenwood/tests/coretest/test/com/android/ravenwoodtest/runnercallbacktests/RavenwoodRunnerCallbackTest.java
@@ -337,9 +337,9 @@
     @Expected("""
     testRunStarted: classes
     testSuiteStarted: classes
-    testStarted: Constructor(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerCallbackTest$ExceptionFromInnerRunnerConstructorTest)
-    testFailure: Exception detected in constructor
-    testFinished: Constructor(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerCallbackTest$ExceptionFromInnerRunnerConstructorTest)
+    testStarted: initializationError(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerCallbackTest$ExceptionFromInnerRunnerConstructorTest)
+    testFailure: Failed to instantiate class androidx.test.ext.junit.runners.AndroidJUnit4
+    testFinished: initializationError(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerCallbackTest$ExceptionFromInnerRunnerConstructorTest)
     testSuiteFinished: classes
     testRunFinished: 1,1,0,0
     """)
@@ -439,9 +439,9 @@
     @Expected("""
     testRunStarted: classes
     testSuiteStarted: classes
-    testStarted: Constructor(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerCallbackTest$BrokenRunnerTest)
-    testFailure: Exception detected in constructor
-    testFinished: Constructor(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerCallbackTest$BrokenRunnerTest)
+    testStarted: initializationError(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerCallbackTest$BrokenRunnerTest)
+    testFailure: Failed to instantiate class com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerCallbackTest$BrokenTestRunner
+    testFinished: initializationError(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerCallbackTest$BrokenRunnerTest)
     testSuiteFinished: classes
     testRunFinished: 1,1,0,0
     """)
diff --git a/ravenwood/tests/coretest/test/com/android/ravenwoodtest/runnercallbacktests/RavenwoodRunnerConfigValidationTest.java b/ravenwood/tests/coretest/test/com/android/ravenwoodtest/runnercallbacktests/RavenwoodRunnerConfigValidationTest.java
index 73ea64f..02d1073 100644
--- a/ravenwood/tests/coretest/test/com/android/ravenwoodtest/runnercallbacktests/RavenwoodRunnerConfigValidationTest.java
+++ b/ravenwood/tests/coretest/test/com/android/ravenwoodtest/runnercallbacktests/RavenwoodRunnerConfigValidationTest.java
@@ -106,17 +106,11 @@
     @Expected("""
     testRunStarted: classes
     testSuiteStarted: classes
-    testStarted: testMethod1(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$ErrorMustBeReportedFromEachTest)
+    testStarted: initializationError(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$ErrorMustBeReportedFromEachTest)
     testFailure: com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$ErrorMustBeReportedFromEachTest.sConfig expected to be public static
-    testFinished: testMethod1(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$ErrorMustBeReportedFromEachTest)
-    testStarted: testMethod2(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$ErrorMustBeReportedFromEachTest)
-    testFailure: com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$ErrorMustBeReportedFromEachTest.sConfig expected to be public static
-    testFinished: testMethod2(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$ErrorMustBeReportedFromEachTest)
-    testStarted: testMethod3(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$ErrorMustBeReportedFromEachTest)
-    testFailure: com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$ErrorMustBeReportedFromEachTest.sConfig expected to be public static
-    testFinished: testMethod3(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$ErrorMustBeReportedFromEachTest)
+    testFinished: initializationError(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$ErrorMustBeReportedFromEachTest)
     testSuiteFinished: classes
-    testRunFinished: 3,3,0,0
+    testRunFinished: 1,1,0,0
     """)
     // CHECKSTYLE:ON
     public static class ErrorMustBeReportedFromEachTest {
@@ -145,9 +139,9 @@
     @Expected("""
     testRunStarted: classes
     testSuiteStarted: classes
-    testStarted: testConfig(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$DuplicateConfigTest)
+    testStarted: initializationError(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$DuplicateConfigTest)
     testFailure: Class com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest.DuplicateConfigTest has multiple fields with @RavenwoodConfig.Config
-    testFinished: testConfig(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$DuplicateConfigTest)
+    testFinished: initializationError(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$DuplicateConfigTest)
     testSuiteFinished: classes
     testRunFinished: 1,1,0,0
     """)
@@ -175,9 +169,9 @@
     @Expected("""
     testRunStarted: classes
     testSuiteStarted: classes
-    testStarted: testConfig(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$NonStaticConfigTest)
+    testStarted: initializationError(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$NonStaticConfigTest)
     testFailure: com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$NonStaticConfigTest.sConfig expected to be public static
-    testFinished: testConfig(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$NonStaticConfigTest)
+    testFinished: initializationError(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$NonStaticConfigTest)
     testSuiteFinished: classes
     testRunFinished: 1,1,0,0
     """)
@@ -201,9 +195,9 @@
     @Expected("""
     testRunStarted: classes
     testSuiteStarted: classes
-    testStarted: testConfig(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$NonPublicConfigTest)
+    testStarted: initializationError(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$NonPublicConfigTest)
     testFailure: com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$NonPublicConfigTest.sConfig expected to be public static
-    testFinished: testConfig(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$NonPublicConfigTest)
+    testFinished: initializationError(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$NonPublicConfigTest)
     testSuiteFinished: classes
     testRunFinished: 1,1,0,0
     """)
@@ -227,9 +221,9 @@
     @Expected("""
     testRunStarted: classes
     testSuiteStarted: classes
-    testStarted: testConfig(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$WrongTypeConfigTest)
+    testStarted: initializationError(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$WrongTypeConfigTest)
     testFailure: Field com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest.WrongTypeConfigTest.sConfig has @RavenwoodConfig.Config but type is not RavenwoodConfig
-    testFinished: testConfig(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$WrongTypeConfigTest)
+    testFinished: initializationError(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$WrongTypeConfigTest)
     testSuiteFinished: classes
     testRunFinished: 1,1,0,0
     """)
@@ -282,9 +276,9 @@
     @Expected("""
     testRunStarted: classes
     testSuiteStarted: classes
-    testStarted: testConfig(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$WithInstanceRuleTest)
+    testStarted: initializationError(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$WithInstanceRuleTest)
     testFailure: RavenwoodConfig and RavenwoodRule cannot be used in the same class. Suggest migrating to RavenwoodConfig.
-    testFinished: testConfig(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$WithInstanceRuleTest)
+    testFinished: initializationError(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$WithInstanceRuleTest)
     testSuiteFinished: classes
     testRunFinished: 1,1,0,0
     """)
@@ -311,9 +305,9 @@
     @Expected("""
     testRunStarted: classes
     testSuiteStarted: classes
-    testStarted: Constructor(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$WithStaticRuleTest)
-    testFailure: Exception detected in constructor
-    testFinished: Constructor(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$WithStaticRuleTest)
+    testStarted: initializationError(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$WithStaticRuleTest)
+    testFailure: Failed to instantiate class androidx.test.ext.junit.runners.AndroidJUnit4
+    testFinished: initializationError(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$WithStaticRuleTest)
     testSuiteFinished: classes
     testRunFinished: 1,1,0,0
     """)
@@ -400,9 +394,9 @@
     @Expected("""
     testRunStarted: classes
     testSuiteStarted: classes
-    testStarted: test(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$ConfigWithRuleInBaseClassTest)
+    testStarted: initializationError(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$ConfigWithRuleInBaseClassTest)
     testFailure: RavenwoodConfig and RavenwoodRule cannot be used in the same class. Suggest migrating to RavenwoodConfig.
-    testFinished: test(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$ConfigWithRuleInBaseClassTest)
+    testFinished: initializationError(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$ConfigWithRuleInBaseClassTest)
     testSuiteFinished: classes
     testRunFinished: 1,1,0,0
     """)
diff --git a/ravenwood/tests/coretest/test/com/android/ravenwoodtest/runnercallbacktests/RavenwoodRunnerTestBase.java b/ravenwood/tests/coretest/test/com/android/ravenwoodtest/runnercallbacktests/RavenwoodRunnerTestBase.java
index 9a6934b..f7a2198 100644
--- a/ravenwood/tests/coretest/test/com/android/ravenwoodtest/runnercallbacktests/RavenwoodRunnerTestBase.java
+++ b/ravenwood/tests/coretest/test/com/android/ravenwoodtest/runnercallbacktests/RavenwoodRunnerTestBase.java
@@ -20,6 +20,7 @@
 
 import android.platform.test.annotations.NoRavenizer;
 import android.platform.test.ravenwood.RavenwoodAwareTestRunner;
+import android.platform.test.ravenwood.RavenwoodConfigPrivate;
 import android.util.Log;
 
 import junitparams.JUnitParamsRunner;
@@ -137,15 +138,14 @@
         // Set a listener to critical errors. This will also prevent
         // {@link RavenwoodAwareTestRunner} from calling System.exit() when there's
         // a critical error.
-        RavenwoodAwareTestRunner.private$ravenwood().setCriticalErrorHandler(
-                listener.sCriticalErrorListener);
+        RavenwoodConfigPrivate.setCriticalErrorHandler(listener.sCriticalErrorListener);
 
         try {
             // Run the test class.
             junitCore.run(testClazz);
         } finally {
             // Clear the critical error listener.
-            RavenwoodAwareTestRunner.private$ravenwood().setCriticalErrorHandler(null);
+            RavenwoodConfigPrivate.setCriticalErrorHandler(null);
         }
 
         // Check the result.
diff --git a/ravenwood/texts/ravenwood-annotation-allowed-classes.txt b/ravenwood/texts/ravenwood-annotation-allowed-classes.txt
index 9c86389..a26fe66 100644
--- a/ravenwood/texts/ravenwood-annotation-allowed-classes.txt
+++ b/ravenwood/texts/ravenwood-annotation-allowed-classes.txt
@@ -359,3 +359,6 @@
 com.android.server.SystemServiceManager
 
 com.android.server.utils.TimingsTraceAndSlog
+
+android.os.IpcDataCache
+android.app.PropertyInvalidatedCache
diff --git a/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/Utils.kt b/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/Utils.kt
index 37a7975..6092fcc 100644
--- a/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/Utils.kt
+++ b/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/Utils.kt
@@ -15,6 +15,7 @@
  */
 package com.android.platform.test.ravenwood.ravenizer
 
+import android.platform.test.annotations.internal.InnerRunner
 import android.platform.test.annotations.NoRavenizer
 import android.platform.test.ravenwood.RavenwoodAwareTestRunner
 import com.android.hoststubgen.asm.ClassNodes
@@ -39,7 +40,7 @@
 val ruleAnotType = TypeHolder(org.junit.Rule::class.java)
 val classRuleAnotType = TypeHolder(org.junit.ClassRule::class.java)
 val runWithAnotType = TypeHolder(RunWith::class.java)
-val innerRunnerAnotType = TypeHolder(RavenwoodAwareTestRunner.InnerRunner::class.java)
+val innerRunnerAnotType = TypeHolder(InnerRunner::class.java)
 val noRavenizerAnotType = TypeHolder(NoRavenizer::class.java)
 
 val testRuleType = TypeHolder(TestRule::class.java)
diff --git a/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/adapter/RunnerRewritingAdapter.kt b/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/adapter/RunnerRewritingAdapter.kt
index cf6d6f6..81fe3da 100644
--- a/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/adapter/RunnerRewritingAdapter.kt
+++ b/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/adapter/RunnerRewritingAdapter.kt
@@ -15,7 +15,6 @@
  */
 package com.android.platform.test.ravenwood.ravenizer.adapter
 
-import android.platform.test.ravenwood.RavenwoodAwareTestRunner
 import com.android.hoststubgen.ClassParseException
 import com.android.hoststubgen.asm.CLASS_INITIALIZER_DESC
 import com.android.hoststubgen.asm.CLASS_INITIALIZER_NAME
@@ -28,8 +27,8 @@
 import com.android.hoststubgen.visitors.OPCODE_VERSION
 import com.android.platform.test.ravenwood.ravenizer.RavenizerInternalException
 import com.android.platform.test.ravenwood.ravenizer.classRuleAnotType
-import com.android.platform.test.ravenwood.ravenizer.isTestLookingClass
 import com.android.platform.test.ravenwood.ravenizer.innerRunnerAnotType
+import com.android.platform.test.ravenwood.ravenizer.isTestLookingClass
 import com.android.platform.test.ravenwood.ravenizer.noRavenizerAnotType
 import com.android.platform.test.ravenwood.ravenizer.ravenwoodTestRunnerType
 import com.android.platform.test.ravenwood.ravenizer.ruleAnotType
@@ -50,7 +49,7 @@
  * Class visitor to update the RunWith and inject some necessary rules.
  *
  * - Change the @RunWith(RavenwoodAwareTestRunner.class).
- * - If the original class has a @RunWith(...), then change it to an @OrigRunWith(...).
+ * - If the original class has a @RunWith(...), then change it to an @InnerRunner(...).
  * - Add RavenwoodAwareTestRunner's member rules as junit rules.
  * - Update the order of the existing JUnit rules to make sure they don't use the MIN or MAX.
  */
@@ -146,7 +145,7 @@
 
     /**
      * Inject `@RunWith(RavenwoodAwareTestRunner.class)`. If the class already has
-     * a `@RunWith`, then change it to add a `@OrigRunWith`.
+     * a `@RunWith`, then change it to add a `@InnerRunner`.
      */
     private fun injectRunWithAnnotation() {
         // Extract the original RunWith annotation and its value.
@@ -172,7 +171,7 @@
                         + " in class ${classInternalName.toHumanReadableClassName()}")
             }
 
-            // Inject an @OrigRunWith.
+            // Inject an @InnerRunner.
             visitAnnotation(innerRunnerAnotType.desc, true)!!.let { av ->
                 av.visit("value", runWithClass)
                 av.visitEnd()
@@ -302,7 +301,7 @@
         override fun visitCode() {
             visitFieldInsn(Opcodes.GETSTATIC,
                 ravenwoodTestRunnerType.internlName,
-                RavenwoodAwareTestRunner.IMPLICIT_CLASS_OUTER_RULE_NAME,
+                IMPLICIT_CLASS_OUTER_RULE_NAME,
                 testRuleType.desc
             )
             visitFieldInsn(Opcodes.PUTSTATIC,
@@ -313,7 +312,7 @@
 
             visitFieldInsn(Opcodes.GETSTATIC,
                 ravenwoodTestRunnerType.internlName,
-                RavenwoodAwareTestRunner.IMPLICIT_CLASS_INNER_RULE_NAME,
+                IMPLICIT_CLASS_INNER_RULE_NAME,
                 testRuleType.desc
             )
             visitFieldInsn(Opcodes.PUTSTATIC,
@@ -361,7 +360,7 @@
             visitVarInsn(ALOAD, 0)
             visitFieldInsn(Opcodes.GETSTATIC,
                 ravenwoodTestRunnerType.internlName,
-                RavenwoodAwareTestRunner.IMPLICIT_INST_OUTER_RULE_NAME,
+                IMPLICIT_INST_OUTER_RULE_NAME,
                 testRuleType.desc
             )
             visitFieldInsn(Opcodes.PUTFIELD,
@@ -373,7 +372,7 @@
             visitVarInsn(ALOAD, 0)
             visitFieldInsn(Opcodes.GETSTATIC,
                 ravenwoodTestRunnerType.internlName,
-                RavenwoodAwareTestRunner.IMPLICIT_INST_INNER_RULE_NAME,
+                IMPLICIT_INST_INNER_RULE_NAME,
                 testRuleType.desc
             )
             visitFieldInsn(Opcodes.PUTFIELD,
@@ -435,6 +434,11 @@
     }
 
     companion object {
+        const val IMPLICIT_CLASS_OUTER_RULE_NAME = "sImplicitClassOuterRule"
+        const val IMPLICIT_CLASS_INNER_RULE_NAME = "sImplicitClassInnerRule"
+        const val IMPLICIT_INST_OUTER_RULE_NAME = "sImplicitInstOuterRule"
+        const val IMPLICIT_INST_INNER_RULE_NAME = "sImplicitInstInnerRule"
+
         fun shouldProcess(classes: ClassNodes, className: String): Boolean {
             if (!isTestLookingClass(classes, className)) {
                 return false
@@ -463,4 +467,4 @@
             }
         }
     }
-}
\ No newline at end of file
+}
diff --git a/services/accessibility/java/com/android/server/accessibility/MouseKeysInterceptor.java b/services/accessibility/java/com/android/server/accessibility/MouseKeysInterceptor.java
index 1df5d1a..1212c75 100644
--- a/services/accessibility/java/com/android/server/accessibility/MouseKeysInterceptor.java
+++ b/services/accessibility/java/com/android/server/accessibility/MouseKeysInterceptor.java
@@ -625,7 +625,9 @@
         });
 
         mHandler.removeCallbacksAndMessages(null);
-        mVirtualDevice.close();
+        if (mVirtualDevice != null) {
+            mVirtualDevice.close();
+        }
     }
 
     @Override
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index f6ac706..f9abd85 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -237,6 +237,10 @@
         @Override
         public void onReceive(Context context, Intent intent) {
             final String action = intent.getAction();
+            if (action == null) {
+                return;
+            }
+
             final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
 
             if (DEBUG) {
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index 51034d2..a960015 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -31,16 +31,13 @@
 
 import static com.android.internal.util.CollectionUtils.any;
 import static com.android.internal.util.Preconditions.checkState;
-import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
 import static com.android.server.companion.utils.PackageUtils.enforceUsesCompanionDeviceFeature;
-import static com.android.server.companion.utils.PackageUtils.getPackageInfo;
 import static com.android.server.companion.utils.PackageUtils.isRestrictedSettingsAllowed;
 import static com.android.server.companion.utils.PermissionsUtils.enforceCallerCanManageAssociationsForPackage;
 import static com.android.server.companion.utils.PermissionsUtils.enforceCallerIsSystemOr;
 import static com.android.server.companion.utils.PermissionsUtils.enforceCallerIsSystemOrCanInteractWithUserId;
 
 import static java.util.Objects.requireNonNull;
-import static java.util.concurrent.TimeUnit.MINUTES;
 
 import android.annotation.EnforcePermission;
 import android.annotation.NonNull;
@@ -69,31 +66,22 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
-import android.content.SharedPreferences;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager;
 import android.content.pm.PackageManagerInternal;
 import android.net.MacAddress;
-import android.net.NetworkPolicyManager;
 import android.os.Binder;
-import android.os.Environment;
 import android.os.Parcel;
 import android.os.ParcelFileDescriptor;
 import android.os.PowerExemptionManager;
 import android.os.PowerManagerInternal;
 import android.os.RemoteException;
-import android.os.ServiceManager;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.permission.flags.Flags;
-import android.util.ArraySet;
 import android.util.ExceptionUtils;
 import android.util.Slog;
 
-import com.android.internal.app.IAppOpsService;
 import com.android.internal.content.PackageMonitor;
 import com.android.internal.notification.NotificationAccessConfirmationActivityContract;
-import com.android.internal.os.BackgroundThread;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.DumpUtils;
 import com.android.server.FgThread;
@@ -114,35 +102,27 @@
 import com.android.server.companion.devicepresence.ObservableUuid;
 import com.android.server.companion.devicepresence.ObservableUuidStore;
 import com.android.server.companion.transport.CompanionTransportManager;
-import com.android.server.pm.UserManagerInternal;
 import com.android.server.wm.ActivityTaskManagerInternal;
 
-import java.io.File;
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.Collection;
 import java.util.List;
-import java.util.Set;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
 
 @SuppressLint("LongLogTag")
 public class CompanionDeviceManagerService extends SystemService {
     private static final String TAG = "CDM_CompanionDeviceManagerService";
 
     private static final long PAIR_WITHOUT_PROMPT_WINDOW_MS = 10 * 60 * 1000; // 10 min
-
-    private static final String PREF_FILE_NAME = "companion_device_preferences.xml";
-    private static final String PREF_KEY_AUTO_REVOKE_GRANTS_DONE = "auto_revoke_grants_done";
     private static final int MAX_CN_LENGTH = 500;
 
-    private final ActivityTaskManagerInternal mAtmInternal;
-    private final ActivityManagerInternal mAmInternal;
-    private final IAppOpsService mAppOpsManager;
-    private final PowerExemptionManager mPowerExemptionManager;
-    private final PackageManagerInternal mPackageManagerInternal;
-
     private final AssociationStore mAssociationStore;
     private final SystemDataTransferRequestStore mSystemDataTransferRequestStore;
     private final ObservableUuidStore mObservableUuidStore;
+
+    private final CompanionExemptionProcessor mCompanionExemptionProcessor;
     private final AssociationRequestsProcessor mAssociationRequestsProcessor;
     private final SystemDataTransferProcessor mSystemDataTransferProcessor;
     private final BackupRestoreProcessor mBackupRestoreProcessor;
@@ -156,12 +136,15 @@
         super(context);
 
         final ActivityManager activityManager = context.getSystemService(ActivityManager.class);
-        mPowerExemptionManager = context.getSystemService(PowerExemptionManager.class);
-        mAppOpsManager = IAppOpsService.Stub.asInterface(
-                ServiceManager.getService(Context.APP_OPS_SERVICE));
-        mAtmInternal = LocalServices.getService(ActivityTaskManagerInternal.class);
-        mAmInternal = LocalServices.getService(ActivityManagerInternal.class);
-        mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
+        final PowerExemptionManager powerExemptionManager = context.getSystemService(
+                PowerExemptionManager.class);
+        final AppOpsManager appOpsManager = context.getSystemService(AppOpsManager.class);
+        final ActivityTaskManagerInternal atmInternal = LocalServices.getService(
+                ActivityTaskManagerInternal.class);
+        final ActivityManagerInternal amInternal = LocalServices.getService(
+                ActivityManagerInternal.class);
+        final PackageManagerInternal packageManagerInternal = LocalServices.getService(
+                PackageManagerInternal.class);
         final UserManager userManager = context.getSystemService(UserManager.class);
         final PowerManagerInternal powerManagerInternal = LocalServices.getService(
                 PowerManagerInternal.class);
@@ -173,25 +156,29 @@
 
         // Init processors
         mAssociationRequestsProcessor = new AssociationRequestsProcessor(context,
-                mPackageManagerInternal, mAssociationStore);
-        mBackupRestoreProcessor = new BackupRestoreProcessor(context, mPackageManagerInternal,
+                packageManagerInternal, mAssociationStore);
+        mBackupRestoreProcessor = new BackupRestoreProcessor(context, packageManagerInternal,
                 mAssociationStore, associationDiskStore, mSystemDataTransferRequestStore,
                 mAssociationRequestsProcessor);
 
         mCompanionAppBinder = new CompanionAppBinder(context);
 
+        mCompanionExemptionProcessor = new CompanionExemptionProcessor(context,
+                powerExemptionManager, appOpsManager, packageManagerInternal, atmInternal,
+                amInternal, mAssociationStore);
+
         mDevicePresenceProcessor = new DevicePresenceProcessor(context,
                 mCompanionAppBinder, userManager, mAssociationStore, mObservableUuidStore,
-                powerManagerInternal);
+                powerManagerInternal, mCompanionExemptionProcessor);
 
         mTransportManager = new CompanionTransportManager(context, mAssociationStore);
 
         mDisassociationProcessor = new DisassociationProcessor(context, activityManager,
-                mAssociationStore, mPackageManagerInternal, mDevicePresenceProcessor,
+                mAssociationStore, packageManagerInternal, mDevicePresenceProcessor,
                 mCompanionAppBinder, mSystemDataTransferRequestStore, mTransportManager);
 
         mSystemDataTransferProcessor = new SystemDataTransferProcessor(this,
-                mPackageManagerInternal, mAssociationStore,
+                packageManagerInternal, mAssociationStore,
                 mSystemDataTransferRequestStore, mTransportManager);
 
         // TODO(b/279663946): move context sync to a dedicated system service
@@ -202,7 +189,6 @@
     public void onStart() {
         // Init association stores
         mAssociationStore.refreshCache();
-        mAssociationStore.registerLocalListener(mAssociationStoreChangeListener);
 
         // Init UUID store
         mObservableUuidStore.getObservableUuidsForUser(getContext().getUserId());
@@ -240,11 +226,11 @@
 
         if (associations.isEmpty()) return;
 
-        updateAtm(userId, associations);
+        mCompanionExemptionProcessor.updateAtm(userId, associations);
 
-        BackgroundThread.getHandler().sendMessageDelayed(
-                obtainMessage(CompanionDeviceManagerService::maybeGrantAutoRevokeExemptions, this),
-                MINUTES.toMillis(10));
+        try (ExecutorService executor = Executors.newSingleThreadExecutor()) {
+            executor.execute(mCompanionExemptionProcessor::updateAutoRevokeExemptions);
+        }
     }
 
     @Override
@@ -262,9 +248,12 @@
         if (!associationsForPackage.isEmpty()) {
             Slog.i(TAG, "Package removed or data cleared for user=[" + userId + "], package=["
                     + packageName + "]. Cleaning up CDM data...");
-        }
-        for (AssociationInfo association : associationsForPackage) {
-            mDisassociationProcessor.disassociate(association.getId());
+
+            for (AssociationInfo association : associationsForPackage) {
+                mDisassociationProcessor.disassociate(association.getId());
+            }
+
+            mCompanionAppBinder.onPackagesChanged(userId, packageName);
         }
 
         // Clear observable UUIDs for the package.
@@ -273,19 +262,16 @@
         for (ObservableUuid uuid : uuidsTobeObserved) {
             mObservableUuidStore.removeObservableUuid(userId, uuid.getUuid(), packageName);
         }
-
-        mCompanionAppBinder.onPackagesChanged(userId);
     }
 
     private void onPackageModifiedInternal(@UserIdInt int userId, @NonNull String packageName) {
-        final List<AssociationInfo> associationsForPackage =
+        final List<AssociationInfo> associations =
                 mAssociationStore.getAssociationsByPackage(userId, packageName);
-        for (AssociationInfo association : associationsForPackage) {
-            updateSpecialAccessPermissionForAssociatedPackage(association.getUserId(),
-                    association.getPackageName());
-        }
+        if (!associations.isEmpty()) {
+            mCompanionExemptionProcessor.exemptPackage(userId, packageName, false);
 
-        mCompanionAppBinder.onPackagesChanged(userId);
+            mCompanionAppBinder.onPackagesChanged(userId, packageName);
+        }
     }
 
     private void onPackageAddedInternal(@UserIdInt int userId, @NonNull String packageName) {
@@ -765,130 +751,6 @@
         }
     }
 
-    /**
-     * Update special access for the association's package
-     */
-    public void updateSpecialAccessPermissionForAssociatedPackage(int userId, String packageName) {
-        final PackageInfo packageInfo =
-                getPackageInfo(getContext(), userId, packageName);
-
-        Binder.withCleanCallingIdentity(() -> updateSpecialAccessPermissionAsSystem(packageInfo));
-    }
-
-    private void updateSpecialAccessPermissionAsSystem(PackageInfo packageInfo) {
-        if (packageInfo == null) {
-            return;
-        }
-
-        if (containsEither(packageInfo.requestedPermissions,
-                android.Manifest.permission.RUN_IN_BACKGROUND,
-                android.Manifest.permission.REQUEST_COMPANION_RUN_IN_BACKGROUND)) {
-            mPowerExemptionManager.addToPermanentAllowList(packageInfo.packageName);
-        } else {
-            try {
-                mPowerExemptionManager.removeFromPermanentAllowList(packageInfo.packageName);
-            } catch (UnsupportedOperationException e) {
-                Slog.w(TAG, packageInfo.packageName + " can't be removed from power save"
-                        + " whitelist. It might due to the package is whitelisted by the system.");
-            }
-        }
-
-        NetworkPolicyManager networkPolicyManager = NetworkPolicyManager.from(getContext());
-        try {
-            if (containsEither(packageInfo.requestedPermissions,
-                    android.Manifest.permission.USE_DATA_IN_BACKGROUND,
-                    android.Manifest.permission.REQUEST_COMPANION_USE_DATA_IN_BACKGROUND)) {
-                networkPolicyManager.addUidPolicy(
-                        packageInfo.applicationInfo.uid,
-                        NetworkPolicyManager.POLICY_ALLOW_METERED_BACKGROUND);
-            } else {
-                networkPolicyManager.removeUidPolicy(
-                        packageInfo.applicationInfo.uid,
-                        NetworkPolicyManager.POLICY_ALLOW_METERED_BACKGROUND);
-            }
-        } catch (IllegalArgumentException e) {
-            Slog.e(TAG, e.getMessage());
-        }
-
-        exemptFromAutoRevoke(packageInfo.packageName, packageInfo.applicationInfo.uid);
-    }
-
-    private void exemptFromAutoRevoke(String packageName, int uid) {
-        try {
-            mAppOpsManager.setMode(
-                    AppOpsManager.OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED,
-                    uid,
-                    packageName,
-                    AppOpsManager.MODE_IGNORED);
-        } catch (RemoteException e) {
-            Slog.w(TAG, "Error while granting auto revoke exemption for " + packageName, e);
-        }
-    }
-
-    private void updateAtm(int userId, List<AssociationInfo> associations) {
-        final Set<Integer> companionAppUids = new ArraySet<>();
-        for (AssociationInfo association : associations) {
-            final int uid = mPackageManagerInternal.getPackageUid(association.getPackageName(),
-                    0, userId);
-            if (uid >= 0) {
-                companionAppUids.add(uid);
-            }
-        }
-        if (mAtmInternal != null) {
-            mAtmInternal.setCompanionAppUids(userId, companionAppUids);
-        }
-        if (mAmInternal != null) {
-            // Make a copy of the set and send it to ActivityManager.
-            mAmInternal.setCompanionAppUids(userId, new ArraySet<>(companionAppUids));
-        }
-    }
-
-    private void maybeGrantAutoRevokeExemptions() {
-        Slog.d(TAG, "maybeGrantAutoRevokeExemptions()");
-
-        PackageManager pm = getContext().getPackageManager();
-        for (int userId : LocalServices.getService(UserManagerInternal.class).getUserIds()) {
-            SharedPreferences pref = getContext().getSharedPreferences(
-                    new File(Environment.getUserSystemDirectory(userId), PREF_FILE_NAME),
-                    Context.MODE_PRIVATE);
-            if (pref.getBoolean(PREF_KEY_AUTO_REVOKE_GRANTS_DONE, false)) {
-                continue;
-            }
-
-            try {
-                final List<AssociationInfo> associations =
-                        mAssociationStore.getActiveAssociationsByUser(userId);
-                for (AssociationInfo a : associations) {
-                    try {
-                        int uid = pm.getPackageUidAsUser(a.getPackageName(), userId);
-                        exemptFromAutoRevoke(a.getPackageName(), uid);
-                    } catch (PackageManager.NameNotFoundException e) {
-                        Slog.w(TAG, "Unknown companion package: " + a.getPackageName(), e);
-                    }
-                }
-            } finally {
-                pref.edit().putBoolean(PREF_KEY_AUTO_REVOKE_GRANTS_DONE, true).apply();
-            }
-        }
-    }
-
-    private final AssociationStore.OnChangeListener mAssociationStoreChangeListener =
-            new AssociationStore.OnChangeListener() {
-                @Override
-                public void onAssociationChanged(int changeType, AssociationInfo association) {
-                    Slog.d(TAG, "onAssociationChanged changeType=[" + changeType
-                            + "], association=[" + association);
-
-                    final int userId = association.getUserId();
-                    final List<AssociationInfo> updatedAssociations =
-                            mAssociationStore.getActiveAssociationsByUser(userId);
-
-                    updateAtm(userId, updatedAssociations);
-                    updateSpecialAccessPermissionForAssociatedPackage(association.getUserId(),
-                            association.getPackageName());
-                }
-            };
-
     private final PackageMonitor mPackageMonitor = new PackageMonitor() {
         @Override
         public void onPackageRemoved(String packageName, int uid) {
@@ -911,10 +773,6 @@
         }
     };
 
-    private static <T> boolean containsEither(T[] array, T a, T b) {
-        return ArrayUtils.contains(array, a) || ArrayUtils.contains(array, b);
-    }
-
     private class LocalService implements CompanionDeviceManagerServiceInternal {
 
         @Override
diff --git a/services/companion/java/com/android/server/companion/CompanionExemptionProcessor.java b/services/companion/java/com/android/server/companion/CompanionExemptionProcessor.java
new file mode 100644
index 0000000..4969ffbf
--- /dev/null
+++ b/services/companion/java/com/android/server/companion/CompanionExemptionProcessor.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.companion;
+
+import static android.app.AppOpsManager.MODE_ALLOWED;
+import static android.app.AppOpsManager.MODE_IGNORED;
+
+import static com.android.server.companion.utils.PackageUtils.getPackageInfo;
+
+import android.annotation.SuppressLint;
+import android.app.ActivityManagerInternal;
+import android.app.AppOpsManager;
+import android.companion.AssociationInfo;
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
+import android.net.NetworkPolicyManager;
+import android.os.Binder;
+import android.os.Environment;
+import android.os.PowerExemptionManager;
+import android.util.ArraySet;
+import android.util.Slog;
+
+import com.android.internal.util.ArrayUtils;
+import com.android.server.LocalServices;
+import com.android.server.companion.association.AssociationStore;
+import com.android.server.pm.UserManagerInternal;
+import com.android.server.wm.ActivityTaskManagerInternal;
+
+import java.io.File;
+import java.util.List;
+import java.util.Set;
+
+@SuppressLint("LongLogTag")
+public class CompanionExemptionProcessor {
+
+    private static final String TAG = "CDM_CompanionExemptionProcessor";
+
+    private static final String PREF_FILE_NAME = "companion_device_preferences.xml";
+    private static final String PREF_KEY_AUTO_REVOKE_GRANTS_DONE = "auto_revoke_grants_done";
+
+    private final Context mContext;
+    private final PowerExemptionManager mPowerExemptionManager;
+    private final AppOpsManager mAppOpsManager;
+    private final PackageManagerInternal mPackageManager;
+    private final ActivityTaskManagerInternal mAtmInternal;
+    private final ActivityManagerInternal mAmInternal;
+    private final AssociationStore mAssociationStore;
+
+    public CompanionExemptionProcessor(Context context, PowerExemptionManager powerExemptionManager,
+            AppOpsManager appOpsManager, PackageManagerInternal packageManager,
+            ActivityTaskManagerInternal atmInternal, ActivityManagerInternal amInternal,
+            AssociationStore associationStore) {
+        mContext = context;
+        mPowerExemptionManager = powerExemptionManager;
+        mAppOpsManager = appOpsManager;
+        mPackageManager = packageManager;
+        mAtmInternal = atmInternal;
+        mAmInternal = amInternal;
+        mAssociationStore = associationStore;
+
+        mAssociationStore.registerLocalListener(new AssociationStore.OnChangeListener() {
+            @Override
+            public void onAssociationChanged(int changeType, AssociationInfo association) {
+                final int userId = association.getUserId();
+                final List<AssociationInfo> updatedAssociations =
+                        mAssociationStore.getActiveAssociationsByUser(userId);
+
+                updateAtm(userId, updatedAssociations);
+            }
+        });
+    }
+
+    /**
+     * Update ActivityManager and ActivityTaskManager exemptions
+     */
+    public void updateAtm(int userId, List<AssociationInfo> associations) {
+        final Set<Integer> companionAppUids = new ArraySet<>();
+        for (AssociationInfo association : associations) {
+            int uid = mPackageManager.getPackageUid(association.getPackageName(), 0, userId);
+            if (uid >= 0) {
+                companionAppUids.add(uid);
+            }
+        }
+        if (mAtmInternal != null) {
+            mAtmInternal.setCompanionAppUids(userId, companionAppUids);
+        }
+        if (mAmInternal != null) {
+            // Make a copy of the set and send it to ActivityManager.
+            mAmInternal.setCompanionAppUids(userId, new ArraySet<>(companionAppUids));
+        }
+    }
+
+    /**
+     * Update special access for the association's package
+     */
+    public void exemptPackage(int userId, String packageName, boolean hasPresentDevices) {
+        Slog.i(TAG, "Exempting package [" + packageName + "]...");
+
+        final PackageInfo packageInfo = getPackageInfo(mContext, userId, packageName);
+
+        Binder.withCleanCallingIdentity(
+                () -> exemptPackageAsSystem(userId, packageInfo, hasPresentDevices));
+    }
+
+    @SuppressLint("MissingPermission")
+    private void exemptPackageAsSystem(int userId, PackageInfo packageInfo,
+            boolean hasPresentDevices) {
+        if (packageInfo == null) {
+            return;
+        }
+
+        // If the app has run-in-bg permission and present devices, add it to power saver allowlist.
+        if (containsEither(packageInfo.requestedPermissions,
+                android.Manifest.permission.RUN_IN_BACKGROUND,
+                android.Manifest.permission.REQUEST_COMPANION_RUN_IN_BACKGROUND)
+                && hasPresentDevices) {
+            mPowerExemptionManager.addToPermanentAllowList(packageInfo.packageName);
+        } else {
+            try {
+                mPowerExemptionManager.removeFromPermanentAllowList(packageInfo.packageName);
+            } catch (UnsupportedOperationException e) {
+                Slog.w(TAG, packageInfo.packageName + " can't be removed from power save"
+                        + " allowlist. It might be due to the package being allowlisted by the"
+                        + " system.");
+            }
+        }
+
+        // If the app has run-in-bg permission and present device, allow metered network use.
+        NetworkPolicyManager networkPolicyManager = NetworkPolicyManager.from(mContext);
+        try {
+            if (containsEither(packageInfo.requestedPermissions,
+                    android.Manifest.permission.USE_DATA_IN_BACKGROUND,
+                    android.Manifest.permission.REQUEST_COMPANION_USE_DATA_IN_BACKGROUND)
+                    && hasPresentDevices) {
+                networkPolicyManager.addUidPolicy(
+                        packageInfo.applicationInfo.uid,
+                        NetworkPolicyManager.POLICY_ALLOW_METERED_BACKGROUND);
+            } else {
+                networkPolicyManager.removeUidPolicy(
+                        packageInfo.applicationInfo.uid,
+                        NetworkPolicyManager.POLICY_ALLOW_METERED_BACKGROUND);
+            }
+        } catch (IllegalArgumentException e) {
+            Slog.e(TAG, e.getMessage());
+        }
+
+        updateAutoRevokeExemption(packageInfo.packageName, packageInfo.applicationInfo.uid,
+                !mAssociationStore.getActiveAssociationsByPackage(userId,
+                        packageInfo.packageName).isEmpty());
+    }
+
+    /**
+     * Update auto revoke exemptions.
+     * If the app has any association, exempt it from permission auto revoke.
+     */
+    public void updateAutoRevokeExemptions() {
+        Slog.d(TAG, "maybeGrantAutoRevokeExemptions()");
+
+        PackageManager pm = mContext.getPackageManager();
+        for (int userId : LocalServices.getService(UserManagerInternal.class).getUserIds()) {
+            SharedPreferences pref = mContext.getSharedPreferences(
+                    new File(Environment.getUserSystemDirectory(userId), PREF_FILE_NAME),
+                    Context.MODE_PRIVATE);
+            if (pref.getBoolean(PREF_KEY_AUTO_REVOKE_GRANTS_DONE, false)) {
+                continue;
+            }
+
+            try {
+                final List<AssociationInfo> associations =
+                        mAssociationStore.getActiveAssociationsByUser(userId);
+                for (AssociationInfo a : associations) {
+                    try {
+                        int uid = pm.getPackageUidAsUser(a.getPackageName(), userId);
+                        updateAutoRevokeExemption(a.getPackageName(), uid, true);
+                    } catch (PackageManager.NameNotFoundException e) {
+                        Slog.w(TAG, "Unknown companion package: " + a.getPackageName(), e);
+                    }
+                }
+            } finally {
+                pref.edit().putBoolean(PREF_KEY_AUTO_REVOKE_GRANTS_DONE, true).apply();
+            }
+        }
+    }
+
+    @SuppressLint("MissingPermission")
+    private void updateAutoRevokeExemption(String packageName, int uid, boolean hasAssociations) {
+        try {
+            mAppOpsManager.setMode(
+                    AppOpsManager.OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED,
+                    uid,
+                    packageName,
+                    hasAssociations ? MODE_IGNORED : MODE_ALLOWED);
+        } catch (Exception e) {
+            Slog.e(TAG, "Error while granting auto revoke exemption for " + packageName, e);
+        }
+    }
+
+    private <T> boolean containsEither(T[] array, T a, T b) {
+        return ArrayUtils.contains(array, a) || ArrayUtils.contains(array, b);
+    }
+
+}
diff --git a/services/companion/java/com/android/server/companion/devicepresence/CompanionAppBinder.java b/services/companion/java/com/android/server/companion/devicepresence/CompanionAppBinder.java
index 60f4688..1eb6d13f 100644
--- a/services/companion/java/com/android/server/companion/devicepresence/CompanionAppBinder.java
+++ b/services/companion/java/com/android/server/companion/devicepresence/CompanionAppBinder.java
@@ -95,7 +95,10 @@
     /**
      * On package changed.
      */
-    public void onPackagesChanged(@UserIdInt int userId) {
+    public void onPackagesChanged(@UserIdInt int userId, String packageName) {
+        // TODO: We shouldn't need to clean up the whole user registry. We only need to remove the
+        //       package. I will do it in a separate change since it's not appropriate to use
+        //       PerUser anymore.
         mCompanionServicesRegister.invalidate(userId);
     }
 
diff --git a/services/companion/java/com/android/server/companion/devicepresence/DevicePresenceProcessor.java b/services/companion/java/com/android/server/companion/devicepresence/DevicePresenceProcessor.java
index a374d27..7b4dd7d 100644
--- a/services/companion/java/com/android/server/companion/devicepresence/DevicePresenceProcessor.java
+++ b/services/companion/java/com/android/server/companion/devicepresence/DevicePresenceProcessor.java
@@ -57,6 +57,7 @@
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.util.CollectionUtils;
+import com.android.server.companion.CompanionExemptionProcessor;
 import com.android.server.companion.association.AssociationStore;
 
 import java.io.PrintWriter;
@@ -101,6 +102,8 @@
     private final PowerManagerInternal mPowerManagerInternal;
     @NonNull
     private final UserManager mUserManager;
+    @NonNull
+    private final CompanionExemptionProcessor mCompanionExemptionProcessor;
 
     // NOTE: Same association may appear in more than one of the following sets at the same time.
     // (E.g. self-managed devices that have MAC addresses, could be reported as present by their
@@ -111,7 +114,7 @@
     @NonNull
     private final Set<Integer> mNearbyBleDevices = new HashSet<>();
     @NonNull
-    private final Set<Integer> mReportedSelfManagedDevices = new HashSet<>();
+    private final Set<Integer> mConnectedSelfManagedDevices = new HashSet<>();
     @NonNull
     private final Set<ParcelUuid> mConnectedUuidDevices = new HashSet<>();
     @NonNull
@@ -146,7 +149,8 @@
             @NonNull UserManager userManager,
             @NonNull AssociationStore associationStore,
             @NonNull ObservableUuidStore observableUuidStore,
-            @NonNull PowerManagerInternal powerManagerInternal) {
+            @NonNull PowerManagerInternal powerManagerInternal,
+            @NonNull CompanionExemptionProcessor companionExemptionProcessor) {
         mContext = context;
         mCompanionAppBinder = companionAppBinder;
         mAssociationStore = associationStore;
@@ -156,6 +160,7 @@
                 mObservableUuidStore, this);
         mBleDeviceProcessor = new BleDeviceProcessor(associationStore, this);
         mPowerManagerInternal = powerManagerInternal;
+        mCompanionExemptionProcessor = companionExemptionProcessor;
     }
 
     /** Initialize {@link DevicePresenceProcessor} */
@@ -404,7 +409,7 @@
      * nearby (for "self-managed" associations).
      */
     public boolean isDevicePresent(int associationId) {
-        return mReportedSelfManagedDevices.contains(associationId)
+        return mConnectedSelfManagedDevices.contains(associationId)
                 || mConnectedBtDevices.contains(associationId)
                 || mNearbyBleDevices.contains(associationId)
                 || mSimulated.contains(associationId);
@@ -451,7 +456,7 @@
      * notifyDeviceAppeared()}
      */
     public void onSelfManagedDeviceConnected(int associationId) {
-        onDevicePresenceEvent(mReportedSelfManagedDevices,
+        onDevicePresenceEvent(mConnectedSelfManagedDevices,
                 associationId, EVENT_SELF_MANAGED_APPEARED);
     }
 
@@ -467,7 +472,7 @@
      * notifyDeviceDisappeared()}
      */
     public void onSelfManagedDeviceDisconnected(int associationId) {
-        onDevicePresenceEvent(mReportedSelfManagedDevices,
+        onDevicePresenceEvent(mConnectedSelfManagedDevices,
                 associationId, EVENT_SELF_MANAGED_DISAPPEARED);
     }
 
@@ -475,7 +480,7 @@
      * Marks a "self-managed" device as disconnected when binderDied.
      */
     public void onSelfManagedDeviceReporterBinderDied(int associationId) {
-        onDevicePresenceEvent(mReportedSelfManagedDevices,
+        onDevicePresenceEvent(mConnectedSelfManagedDevices,
                 associationId, EVENT_SELF_MANAGED_DISAPPEARED);
     }
 
@@ -683,6 +688,7 @@
 
                 if (association.shouldBindWhenPresent()) {
                     bindApplicationIfNeeded(userId, packageName, association.isSelfManaged());
+                    mCompanionExemptionProcessor.exemptPackage(userId, packageName, true);
                 } else {
                     return;
                 }
@@ -715,6 +721,7 @@
                 // Check if there are other devices associated to the app that are present.
                 if (!shouldBindPackage(userId, packageName)) {
                     mCompanionAppBinder.unbindCompanionApp(userId, packageName);
+                    mCompanionExemptionProcessor.exemptPackage(userId, packageName, false);
                 }
                 break;
             default:
@@ -940,7 +947,7 @@
 
         mConnectedBtDevices.remove(id);
         mNearbyBleDevices.remove(id);
-        mReportedSelfManagedDevices.remove(id);
+        mConnectedSelfManagedDevices.remove(id);
         mSimulated.remove(id);
         synchronized (mBtDisconnectedDevices) {
             mBtDisconnectedDevices.remove(id);
@@ -1100,7 +1107,7 @@
         out.append("Companion Device Present: ");
         if (mConnectedBtDevices.isEmpty()
                 && mNearbyBleDevices.isEmpty()
-                && mReportedSelfManagedDevices.isEmpty()) {
+                && mConnectedSelfManagedDevices.isEmpty()) {
             out.append("<empty>\n");
             return;
         } else {
@@ -1130,11 +1137,11 @@
         }
 
         out.append("  Self-Reported Devices: ");
-        if (mReportedSelfManagedDevices.isEmpty()) {
+        if (mConnectedSelfManagedDevices.isEmpty()) {
             out.append("<empty>\n");
         } else {
             out.append("\n");
-            for (int associationId : mReportedSelfManagedDevices) {
+            for (int associationId : mConnectedSelfManagedDevices) {
                 AssociationInfo a = mAssociationStore.getAssociationById(associationId);
                 out.append("    ").append(a.toShortString()).append('\n');
             }
diff --git a/services/companion/java/com/android/server/companion/virtual/SensorController.java b/services/companion/java/com/android/server/companion/virtual/SensorController.java
index 8d075db..88b7910 100644
--- a/services/companion/java/com/android/server/companion/virtual/SensorController.java
+++ b/services/companion/java/com/android/server/companion/virtual/SensorController.java
@@ -256,8 +256,15 @@
                 Slog.e(TAG, "Received invalid ParcelFileDescriptor");
                 return BAD_VALUE;
             }
+
+            SharedMemory sharedMemory;
+            try {
+                sharedMemory = SharedMemory.fromFileDescriptor(fd);
+            } catch (IllegalArgumentException e) {
+                Slog.e(TAG, "Failed to create shared memory: " + e);
+                return BAD_VALUE;
+            }
             final int channelHandle = sNextDirectChannelHandle.getAndIncrement();
-            SharedMemory sharedMemory = SharedMemory.fromFileDescriptor(fd);
             try {
                 mCallback.onDirectChannelCreated(channelHandle, sharedMemory);
             } catch (RemoteException e) {
diff --git a/services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsManagerService.java b/services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsManagerService.java
index a7aab49..84aa331 100644
--- a/services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsManagerService.java
+++ b/services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsManagerService.java
@@ -30,8 +30,6 @@
 import android.app.contentsuggestions.SelectionsRequest;
 import android.content.Context;
 import android.graphics.Bitmap;
-import android.graphics.ColorSpace;
-import android.hardware.HardwareBuffer;
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.RemoteException;
@@ -147,6 +145,7 @@
             }
         }
 
+        @SuppressWarnings("GuardedBy")
         @Override
         public void provideContextImage(
                 int userId,
@@ -157,9 +156,6 @@
             }
             enforceCaller(UserHandle.getCallingUserId(), "provideContextImage");
 
-            HardwareBuffer snapshotBuffer = null;
-            int colorSpaceId = 0;
-
             TaskSnapshot snapshot = null;
             // Skip taking TaskSnapshot when bitmap is provided.
             if (!imageContextRequestExtras.containsKey(ContentSuggestionsManager.EXTRA_BITMAP)) {
@@ -167,29 +163,18 @@
                 snapshot = mActivityTaskManagerInternal.getTaskSnapshotBlocking(
                         taskId, false /* isLowResolution */,
                         TaskSnapshot.REFERENCE_CONTENT_SUGGESTION);
-                if (snapshot != null) {
-                    snapshotBuffer = snapshot.getHardwareBuffer();
-                    ColorSpace colorSpace = snapshot.getColorSpace();
-                    if (colorSpace != null) {
-                        colorSpaceId = colorSpace.getId();
-                    }
-                }
             }
 
             synchronized (mLock) {
                 final ContentSuggestionsPerUserService service = getServiceForUserLocked(userId);
                 if (service != null) {
-                    service.provideContextImageLocked(taskId, snapshotBuffer, colorSpaceId,
-                            imageContextRequestExtras);
+                    service.provideContextImageLocked(taskId, snapshot, imageContextRequestExtras);
                 } else {
                     if (VERBOSE) {
                         Slog.v(TAG, "provideContextImageLocked: no service for " + userId);
                     }
                 }
             }
-            if (snapshot != null) {
-                snapshot.removeReference(TaskSnapshot.REFERENCE_CONTENT_SUGGESTION);
-            }
         }
 
         @Override
diff --git a/services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsPerUserService.java b/services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsPerUserService.java
index c8588e2..6f543a5 100644
--- a/services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsPerUserService.java
+++ b/services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsPerUserService.java
@@ -27,15 +27,13 @@
 import android.content.ComponentName;
 import android.content.pm.PackageManager;
 import android.content.pm.ServiceInfo;
-import android.hardware.HardwareBuffer;
 import android.os.Bundle;
 import android.os.RemoteException;
 import android.util.Slog;
+import android.window.TaskSnapshot;
 
 import com.android.internal.annotations.GuardedBy;
-import com.android.server.LocalServices;
 import com.android.server.infra.AbstractPerUserSystemService;
-import com.android.server.wm.ActivityTaskManagerInternal;
 
 /**
  * Per user delegate of {@link ContentSuggestionsManagerService}.
@@ -52,13 +50,9 @@
     @GuardedBy("mLock")
     private RemoteContentSuggestionsService mRemoteService;
 
-    @NonNull
-    private final ActivityTaskManagerInternal mActivityTaskManagerInternal;
-
     ContentSuggestionsPerUserService(
             ContentSuggestionsManagerService master, Object lock, int userId) {
         super(master, lock, userId);
-        mActivityTaskManagerInternal = LocalServices.getService(ActivityTaskManagerInternal.class);
     }
 
     @GuardedBy("mLock")
@@ -94,16 +88,15 @@
     @GuardedBy("mLock")
     void provideContextImageFromBitmapLocked(@NonNull Bundle bitmapContainingExtras) {
         // No task or snapshot provided, the bitmap is contained in the extras
-        provideContextImageLocked(-1, null, 0, bitmapContainingExtras);
+        provideContextImageLocked(-1, null, bitmapContainingExtras);
     }
 
     @GuardedBy("mLock")
-    void provideContextImageLocked(int taskId, @Nullable HardwareBuffer snapshot,
-            int colorSpaceIdForSnapshot, @NonNull Bundle imageContextRequestExtras) {
+    void provideContextImageLocked(int taskId, @Nullable TaskSnapshot snapshot,
+            @NonNull Bundle imageContextRequestExtras) {
         RemoteContentSuggestionsService service = ensureRemoteServiceLocked();
         if (service != null) {
-            service.provideContextImage(taskId, snapshot, colorSpaceIdForSnapshot,
-                    imageContextRequestExtras);
+            service.provideContextImage(taskId, snapshot, imageContextRequestExtras);
         }
     }
 
diff --git a/services/contentsuggestions/java/com/android/server/contentsuggestions/RemoteContentSuggestionsService.java b/services/contentsuggestions/java/com/android/server/contentsuggestions/RemoteContentSuggestionsService.java
index 6e0a0da..92fa3b2 100644
--- a/services/contentsuggestions/java/com/android/server/contentsuggestions/RemoteContentSuggestionsService.java
+++ b/services/contentsuggestions/java/com/android/server/contentsuggestions/RemoteContentSuggestionsService.java
@@ -24,12 +24,12 @@
 import android.app.contentsuggestions.SelectionsRequest;
 import android.content.ComponentName;
 import android.content.Context;
-import android.hardware.HardwareBuffer;
 import android.os.Bundle;
 import android.os.IBinder;
 import android.service.contentsuggestions.ContentSuggestionsService;
 import android.service.contentsuggestions.IContentSuggestionsService;
 import android.text.format.DateUtils;
+import android.window.TaskSnapshot;
 
 import com.android.internal.infra.AbstractMultiplePendingRequestsRemoteService;
 
@@ -67,10 +67,14 @@
         return TIMEOUT_REMOTE_REQUEST_MILLIS;
     }
 
-    void provideContextImage(int taskId, @Nullable HardwareBuffer contextImage,
-            int colorSpaceId, @NonNull Bundle imageContextRequestExtras) {
-        scheduleAsyncRequest((s) -> s.provideContextImage(taskId, contextImage,
-                colorSpaceId, imageContextRequestExtras));
+    void provideContextImage(int taskId, @Nullable TaskSnapshot snapshot,
+            @NonNull Bundle imageContextRequestExtras) {
+        scheduleAsyncRequest((s) -> {
+            s.provideContextImage(taskId, snapshot, imageContextRequestExtras);
+            if (snapshot != null) {
+                snapshot.removeReference(TaskSnapshot.REFERENCE_CONTENT_SUGGESTION);
+            }
+        });
     }
 
     void suggestContentSelections(
diff --git a/services/core/java/com/android/server/BatteryService.java b/services/core/java/com/android/server/BatteryService.java
index 59dea09..78bc658 100644
--- a/services/core/java/com/android/server/BatteryService.java
+++ b/services/core/java/com/android/server/BatteryService.java
@@ -42,6 +42,7 @@
 import android.os.Binder;
 import android.os.Build;
 import android.os.Bundle;
+import android.os.ConditionVariable;
 import android.os.DropBoxManager;
 import android.os.FileUtils;
 import android.os.Handler;
@@ -144,7 +145,7 @@
     private final Handler mHandler;
 
     private final Object mLock = new Object();
-
+    private final ConditionVariable mConditionVariable = new ConditionVariable();
     private HealthInfo mHealthInfo;
     private final HealthInfo mLastHealthInfo = new HealthInfo();
     private boolean mBatteryLevelCritical;
@@ -379,17 +380,10 @@
         // existing service in a near future. Wait for this.update() to instantiate
         // the initial mHealthInfo.
         long beforeWait = SystemClock.uptimeMillis();
-        synchronized (mLock) {
-            while (mHealthInfo == null) {
-                Slog.i(TAG, "health: Waited " + (SystemClock.uptimeMillis() - beforeWait) +
-                        "ms for callbacks. Waiting another " + HEALTH_HAL_WAIT_MS + " ms...");
-                try {
-                    mLock.wait(HEALTH_HAL_WAIT_MS);
-                } catch (InterruptedException ex) {
-                    Slog.i(TAG, "health: InterruptedException when waiting for update. "
-                        + " Continuing...");
-                }
-            }
+        if (mHealthInfo == null) {
+            Slog.i(TAG, "health: Waited " + (SystemClock.uptimeMillis() - beforeWait)
+                    + "ms for callbacks. Waiting another " + HEALTH_HAL_WAIT_MS + " ms...");
+            mConditionVariable.block(HEALTH_HAL_WAIT_MS);
         }
 
         Slog.i(TAG, "health: Waited " + (SystemClock.uptimeMillis() - beforeWait)
@@ -535,7 +529,7 @@
                 mHealthInfo = info;
                 // Process the new values.
                 processValuesLocked(false);
-                mLock.notifyAll(); // for any waiters on new info
+                mConditionVariable.open();
             } else {
                 copyV1Battery(mLastHealthInfo, info);
             }
@@ -1117,6 +1111,8 @@
             getSetOptions += "|current_now|current_average";
         }
         pw.println("  get [-f] [" + getSetOptions + "]");
+        pw.println("    Gets the value of a battery state.");
+        pw.println("    -f: force to get the latest property value.");
         pw.println("  set [-f] [" + getSetOptions + "] <value>");
         pw.println("    Force a battery property value, freezing battery state.");
         pw.println("    -f: force a battery change broadcast be sent, prints new sequence.");
@@ -1163,8 +1159,15 @@
                 if (key == null) {
                     pw.println("No property specified");
                     return -1;
-
                 }
+
+                // Update the health info.
+                if ((opts & OPTION_FORCE_UPDATE) != 0) {
+                    mConditionVariable.close();
+                    updateHealthInfo();
+                    mConditionVariable.block(HEALTH_HAL_WAIT_MS);
+                }
+
                 switch (key) {
                     case "present":
                         pw.println(mHealthInfo.batteryPresent);
@@ -1192,17 +1195,11 @@
                         break;
                     case "current_now":
                         if (batteryServiceSupportCurrentAdbCommand()) {
-                            if ((opts & OPTION_FORCE_UPDATE) != 0) {
-                                updateHealthInfo();
-                            }
                             pw.println(mHealthInfo.batteryCurrentMicroamps);
                         }
                         break;
                     case "current_average":
                         if (batteryServiceSupportCurrentAdbCommand()) {
-                            if ((opts & OPTION_FORCE_UPDATE) != 0) {
-                                updateHealthInfo();
-                            }
                             pw.println(mHealthInfo.batteryCurrentAverageMicroamps);
                         }
                         break;
diff --git a/services/core/java/com/android/server/SystemConfig.java b/services/core/java/com/android/server/SystemConfig.java
index 6a1e319..1082e62 100644
--- a/services/core/java/com/android/server/SystemConfig.java
+++ b/services/core/java/com/android/server/SystemConfig.java
@@ -670,6 +670,17 @@
     }
 
     private void readAllPermissions() {
+        readAllPermissionsFromXml();
+        readAllPermissionsFromEnvironment();
+
+        // Apply global feature removal last, after all features have been read.
+        // This only needs to happen once.
+        for (String featureName : mUnavailableFeatures) {
+            removeFeature(featureName);
+        }
+    }
+
+    private void readAllPermissionsFromXml() {
         final XmlPullParser parser = Xml.newPullParser();
 
         // Read configuration from system
@@ -1730,7 +1741,13 @@
         } finally {
             IoUtils.closeQuietly(permReader);
         }
+    }
 
+    // Add features or permission dependent on global system properties (as
+    // opposed to XML permission files).
+    // This only needs to be called once after all features have been parsed
+    // from various partition/apex sources.
+    private void readAllPermissionsFromEnvironment() {
         // Some devices can be field-converted to FBE, so offer to splice in
         // those features if not already defined by the static config
         if (StorageManager.isFileEncrypted()) {
@@ -1771,10 +1788,6 @@
                 addFeature(PackageManager.FEATURE_EROFS_LEGACY, 0);
             }
         }
-
-        for (String featureName : mUnavailableFeatures) {
-            removeFeature(featureName);
-        }
     }
 
     private @Nullable SignedPackage parseEnhancedConfirmationTrustedPackage(XmlPullParser parser,
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index 0ca3b56..679c7ac 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -112,6 +112,7 @@
 import com.android.internal.notification.SystemNotificationChannels;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.DumpUtils;
+import com.android.internal.util.FrameworkStatsLog;
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.internal.util.Preconditions;
 import com.android.modules.expresslog.Histogram;
@@ -1226,7 +1227,17 @@
                 // been re-enabled (after being updated for example), then we just overwrite the old
                 // values.
                 for (Entry<String, Integer> entry : knownAuth.entrySet()) {
-                    accountsDb.insertOrReplaceMetaAuthTypeAndUid(entry.getKey(), entry.getValue());
+                    String type = entry.getKey();
+                    Integer newUid = entry.getValue();
+                    if (!Objects.equals(metaAuthUid.get(type), newUid)) {
+                        FrameworkStatsLog.write(
+                                FrameworkStatsLog.ACCOUNT_MANAGER_EVENT,
+                                type,
+                                newUid,
+                                FrameworkStatsLog
+                                        .ACCOUNT_MANAGER_EVENT__EVENT_TYPE__AUTHENTICATOR_ADDED);
+                    }
+                    accountsDb.insertOrReplaceMetaAuthTypeAndUid(type, newUid);
                 }
 
                 final Map<Long, Account> accountsMap = accountsDb.findAllDeAccounts();
@@ -1945,6 +1956,11 @@
                     }
                     accounts.accountsDb.setTransactionSuccessful();
 
+                    FrameworkStatsLog.write(
+                            FrameworkStatsLog.ACCOUNT_MANAGER_EVENT,
+                            account.type,
+                            callingUid,
+                            FrameworkStatsLog.ACCOUNT_MANAGER_EVENT__EVENT_TYPE__ACCOUNT_ADDED);
                     logRecord(AccountsDb.DEBUG_ACTION_ACCOUNT_ADD, AccountsDb.TABLE_ACCOUNTS,
                             accountId,
                             accounts, callingUid);
@@ -2544,6 +2560,11 @@
                     }
                     String action = userUnlocked ? AccountsDb.DEBUG_ACTION_ACCOUNT_REMOVE
                             : AccountsDb.DEBUG_ACTION_ACCOUNT_REMOVE_DE;
+                    FrameworkStatsLog.write(
+                            FrameworkStatsLog.ACCOUNT_MANAGER_EVENT,
+                            account.type,
+                            callingUid,
+                            FrameworkStatsLog.ACCOUNT_MANAGER_EVENT__EVENT_TYPE__ACCOUNT_REMOVED);
                     logRecord(action, AccountsDb.TABLE_ACCOUNTS, accountId, accounts);
                 }
             }
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 3e03045..2a9cb43 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -134,6 +134,7 @@
 import static android.util.FeatureFlagUtils.SETTINGS_ENABLE_MONITOR_PHANTOM_PROCS;
 import static android.view.Display.INVALID_DISPLAY;
 
+import static com.android.internal.util.FrameworkStatsLog.INTENT_CREATOR_TOKEN_ADDED;
 import static com.android.internal.util.FrameworkStatsLog.UNSAFE_INTENT_EVENT_REPORTED__EVENT_TYPE__NEW_MUTABLE_IMPLICIT_PENDING_INTENT_RETRIEVED;
 import static com.android.sdksandbox.flags.Flags.sdkSandboxInstrumentationInfo;
 import static com.android.server.am.ActiveServices.FGS_SAW_RESTRICTIONS;
@@ -2772,8 +2773,12 @@
             // Add common services.
             // IMPORTANT: Before adding services here, make sure ephemeral apps can access them too.
             // Enable the check in ApplicationThread.bindApplication() to make sure.
-            if (!android.server.Flags.removeJavaServiceManagerCache()) {
-                addServiceToMap(mAppBindArgs, "permissionmgr");
+
+            // Removing User Service and App Ops Service from cache breaks boot for auto.
+            // Removing permissionmgr breaks tests for Android Auto due to SELinux restrictions.
+            // TODO: fix SELinux restrictions and remove caching for Android Auto.
+            if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)
+                    || !android.server.Flags.removeJavaServiceManagerCache()) {
                 addServiceToMap(mAppBindArgs, Context.ALARM_SERVICE);
                 addServiceToMap(mAppBindArgs, Context.DISPLAY_SERVICE);
                 addServiceToMap(mAppBindArgs, Context.NETWORKMANAGEMENT_SERVICE);
@@ -2794,12 +2799,12 @@
             // See b/79378449
             // Getting the window service and package service binder from servicemanager
             // is blocked for Apps. However they are necessary for apps.
-            // Removing User Service and App Ops Service from cache breaks boot for auto.
             // TODO: remove exception
-            addServiceToMap(mAppBindArgs, Context.APP_OPS_SERVICE);
             addServiceToMap(mAppBindArgs, "package");
             addServiceToMap(mAppBindArgs, Context.WINDOW_SERVICE);
             addServiceToMap(mAppBindArgs, Context.USER_SERVICE);
+            addServiceToMap(mAppBindArgs, "permissionmgr");
+            addServiceToMap(mAppBindArgs, Context.APP_OPS_SERVICE);
         }
         return mAppBindArgs;
     }
@@ -5551,6 +5556,8 @@
         if (target instanceof PendingIntentRecord) {
             final PendingIntentRecord originalRecord = (PendingIntentRecord) target;
 
+            addCreatorToken(intent, originalRecord.getPackageName());
+
             // In multi-display scenarios, there can be background users who execute the
             // PendingIntent. In these scenarios, we don't want to use the foreground user as the
             // current user.
@@ -19285,12 +19292,14 @@
                             + "} does not correspond to an intent in the extra bundle.");
                     continue;
                 }
-                Slog.wtf(TAG,
-                        "A creator token is added to an intent. creatorPackage: " + creatorPackage
-                                + "; intent: " + intent);
-                IBinder creatorToken = createIntentCreatorToken(extraIntent, creatorPackage);
+                IntentCreatorToken creatorToken = createIntentCreatorToken(extraIntent,
+                        creatorPackage);
                 if (creatorToken != null) {
                     extraIntent.setCreatorToken(creatorToken);
+                    Slog.wtf(TAG, "A creator token is added to an intent. creatorPackage: "
+                            + creatorPackage + "; intent: " + intent);
+                    FrameworkStatsLog.write(INTENT_CREATOR_TOKEN_ADDED,
+                            creatorToken.getCreatorUid());
                 }
             } catch (Exception e) {
                 Slog.wtf(TAG,
@@ -19301,7 +19310,7 @@
         }
     }
 
-    private IBinder createIntentCreatorToken(Intent intent, String creatorPackage) {
+    private IntentCreatorToken createIntentCreatorToken(Intent intent, String creatorPackage) {
         if (IntentCreatorToken.isValid(intent)) return null;
         int creatorUid = getCallingUid();
         IntentCreatorToken.Key key = new IntentCreatorToken.Key(creatorUid, creatorPackage, intent);
diff --git a/services/core/java/com/android/server/am/AppRestrictionController.java b/services/core/java/com/android/server/am/AppRestrictionController.java
index 4c87e1c..c036605 100644
--- a/services/core/java/com/android/server/am/AppRestrictionController.java
+++ b/services/core/java/com/android/server/am/AppRestrictionController.java
@@ -373,7 +373,10 @@
         @Override
         public void onReceive(Context context, Intent intent) {
             final String action = intent.getAction();
-            switch (intent.getAction()) {
+            if (action == null) {
+                return;
+            }
+            switch (action) {
                 case Intent.ACTION_PACKAGE_ADDED: {
                     if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
                         final int uid = intent.getIntExtra(Intent.EXTRA_UID, -1);
diff --git a/services/core/java/com/android/server/am/BroadcastController.java b/services/core/java/com/android/server/am/BroadcastController.java
index a00cac6..71ddf05 100644
--- a/services/core/java/com/android/server/am/BroadcastController.java
+++ b/services/core/java/com/android/server/am/BroadcastController.java
@@ -554,7 +554,7 @@
             }
             BroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage, callerFeatureId,
                     receiverId, permission, callingUid, userId, instantApp, visibleToInstantApps,
-                    exported);
+                    exported, mService.mPlatformCompat);
             if (rl.containsFilter(filter)) {
                 Slog.w(TAG, "Receiver with filter " + filter
                         + " already registered for pid " + rl.pid
diff --git a/services/core/java/com/android/server/am/BroadcastFilter.java b/services/core/java/com/android/server/am/BroadcastFilter.java
index adb2392..f016590 100644
--- a/services/core/java/com/android/server/am/BroadcastFilter.java
+++ b/services/core/java/com/android/server/am/BroadcastFilter.java
@@ -17,16 +17,33 @@
 package com.android.server.am;
 
 import android.annotation.Nullable;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledSince;
+import android.compat.annotation.Overridable;
 import android.content.IntentFilter;
+import android.os.UserHandle;
 import android.util.PrintWriterPrinter;
 import android.util.Printer;
 import android.util.proto.ProtoOutputStream;
 
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.compat.PlatformCompat;
+
 import dalvik.annotation.optimization.NeverCompile;
 
 import java.io.PrintWriter;
 
 public final class BroadcastFilter extends IntentFilter {
+    /**
+     * Limit priority values defined by non-system apps to
+     * ({@link IntentFilter#SYSTEM_LOW_PRIORITY}, {@link IntentFilter#SYSTEM_HIGH_PRIORITY}).
+     */
+    @ChangeId
+    @EnabledSince(targetSdkVersion = android.os.Build.VERSION_CODES.BASE)
+    @Overridable
+    @VisibleForTesting
+    static final long CHANGE_RESTRICT_PRIORITY_VALUES = 371309185L;
+
     // Back-pointer to the list this filter is in.
     final ReceiverList receiverList;
     final String packageName;
@@ -38,11 +55,12 @@
     final boolean instantApp;
     final boolean visibleToInstantApp;
     public final boolean exported;
+    final int initialPriority;
 
     BroadcastFilter(IntentFilter _filter, ReceiverList _receiverList,
             String _packageName, String _featureId, String _receiverId, String _requiredPermission,
             int _owningUid, int _userId, boolean _instantApp, boolean _visibleToInstantApp,
-            boolean _exported) {
+            boolean _exported, PlatformCompat platformCompat) {
         super(_filter);
         receiverList = _receiverList;
         packageName = _packageName;
@@ -54,6 +72,8 @@
         instantApp = _instantApp;
         visibleToInstantApp = _visibleToInstantApp;
         exported = _exported;
+        initialPriority = getPriority();
+        setPriority(calculateAdjustedPriority(owningUid, initialPriority, platformCompat));
     }
 
     public @Nullable String getReceiverClassName() {
@@ -100,6 +120,29 @@
         if (requiredPermission != null) {
             pw.print(prefix); pw.print("requiredPermission="); pw.println(requiredPermission);
         }
+        if (initialPriority != getPriority()) {
+            pw.print(prefix); pw.print("initialPriority="); pw.println(initialPriority);
+        }
+    }
+
+    @VisibleForTesting
+    static int calculateAdjustedPriority(int owningUid, int priority,
+            PlatformCompat platformCompat) {
+        if (!Flags.restrictPriorityValues()) {
+            return priority;
+        }
+        if (!platformCompat.isChangeEnabledByUidInternalNoLogging(
+                CHANGE_RESTRICT_PRIORITY_VALUES, owningUid)) {
+            return priority;
+        }
+        if (!UserHandle.isCore(owningUid)) {
+            if (priority >= SYSTEM_HIGH_PRIORITY) {
+                return SYSTEM_HIGH_PRIORITY - 1;
+            } else if (priority <= SYSTEM_LOW_PRIORITY) {
+                return SYSTEM_LOW_PRIORITY + 1;
+            }
+        }
+        return priority;
     }
 
     public String toString() {
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index 8d16eb5..de3e2c9 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -78,7 +78,6 @@
 import static android.os.Process.THREAD_GROUP_TOP_APP;
 import static android.os.Process.THREAD_PRIORITY_DISPLAY;
 import static android.os.Process.THREAD_PRIORITY_TOP_APP_BOOST;
-import static android.os.Process.setProcessGroup;
 
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ALL;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BACKUP;
@@ -526,7 +525,7 @@
                         + msg.obj + " to " + group);
             }
             try {
-                setProcessGroup(pid, group);
+                android.os.Process.setProcessGroup(pid, group);
             } catch (Exception e) {
                 if (DEBUG_ALL) {
                     Slog.w(TAG, "Failed setting process group of " + pid + " to " + group, e);
@@ -544,6 +543,11 @@
                 / CACHED_APP_IMPORTANCE_LEVELS;
     }
 
+    void setProcessGroup(int pid, int group, String processName) {
+        mProcessGroupHandler.sendMessage(mProcessGroupHandler.obtainMessage(
+                0 /* unused */, pid, group, processName));
+    }
+
     void initSettings() {
         mCachedAppOptimizer.init();
         mCacheOomRanker.init(ActivityThread.currentApplication().getMainExecutor());
@@ -3491,8 +3495,8 @@
                     processGroup = THREAD_GROUP_DEFAULT;
                     break;
             }
-            mProcessGroupHandler.sendMessage(mProcessGroupHandler.obtainMessage(
-                    0 /* unused */, app.getPid(), processGroup, app.processName));
+            setProcessGroup(app.getPid(), processGroup, app.processName);
+            mService.mPhantomProcessList.setProcessGroupForPhantomProcessOfApp(app, processGroup);
             try {
                 final int renderThreadTid = app.getRenderThreadTid();
                 if (curSchedGroup == SCHED_GROUP_TOP_APP) {
@@ -4064,8 +4068,9 @@
     }
 
     /**
-     * Evaluate the service connection, return {@code true} if the client will change the state
-     * of the service host process by the given connection.
+     * Evaluate the service connection, return {@code true} if the client will change any state
+     * (ie. ProcessState, oomAdj, capability, etc) of the service host process by the given
+     * connection.
      */
     @GuardedBy("mService")
     boolean evaluateServiceConnectionAdd(ProcessRecord client, ProcessRecord app,
@@ -4073,20 +4078,40 @@
         if (evaluateConnectionPrelude(client, app)) {
             return true;
         }
-        if (app.getSetAdj() <= client.getSetAdj()
-                && app.getSetProcState() <= client.getSetProcState()
-                && ((app.getSetCapability() & client.getSetCapability())
-                        == client.getSetCapability()
-                        || cr.notHasFlag(Context.BIND_INCLUDE_CAPABILITIES
-                                | Context.BIND_BYPASS_USER_NETWORK_RESTRICTIONS))) {
-            // The service host process has better states than the client, so no change.
-            return false;
+
+        boolean needDryRun = false;
+        if (app.getSetAdj() > client.getSetAdj()) {
+            // The connection might elevate the importance of the service's oom adj score.
+            needDryRun = true;
+        } else if (app.getSetProcState() > client.getSetProcState()) {
+            // The connection might elevate the importance of the service's process state.
+            needDryRun = true;
+        } else if (cr.hasFlag(Context.BIND_INCLUDE_CAPABILITIES
+                            | Context.BIND_BYPASS_USER_NETWORK_RESTRICTIONS)
+                && (app.getSetCapability() & client.getSetCapability())
+                        != client.getSetCapability()) {
+            // The connection might elevate the importance of the service's capabilities.
+            needDryRun = true;
+        } else if (Flags.unfreezeBindPolicyFix()
+                && cr.hasFlag(Context.BIND_WAIVE_PRIORITY
+                            | Context.BIND_ALLOW_OOM_MANAGEMENT)) {
+            // These bind flags can grant the shouldNotFreeze state to the service.
+            needDryRun = true;
+        } else if (Flags.unfreezeBindPolicyFix()
+                && client.mOptRecord.shouldNotFreeze()
+                && !app.mOptRecord.shouldNotFreeze()) {
+            // The shouldNotFreeze state can be propagated and needs to be checked.
+            needDryRun = true;
         }
-        // Take a dry run of the computeServiceHostOomAdjLSP, this would't be expensive
-        // since it's only evaluating one service connection.
-        return computeServiceHostOomAdjLSP(cr, app, client, mInjector.getUptimeMillis(),
-                mService.getTopApp(), false, false, false, OOM_ADJ_REASON_NONE,
-                CACHED_APP_MIN_ADJ, false, true /* dryRun */);
+
+        if (needDryRun) {
+            // Take a dry run of the computeServiceHostOomAdjLSP, this would't be expensive
+            // since it's only evaluating one service connection.
+            return computeServiceHostOomAdjLSP(cr, app, client, mInjector.getUptimeMillis(),
+                    mService.getTopApp(), false, false, false, OOM_ADJ_REASON_NONE,
+                    CACHED_APP_MIN_ADJ, false, true /* dryRun */);
+        }
+        return false;
     }
 
     @GuardedBy("mService")
@@ -4096,17 +4121,26 @@
             return true;
         }
 
-        if (app.getSetAdj() < client.getSetAdj()
-                && app.getSetProcState() < client.getSetProcState()) {
-            // The service host process has better states than the client.
-            if (((app.getSetCapability() & client.getSetCapability()) == PROCESS_CAPABILITY_NONE)
-                    || cr.notHasFlag(Context.BIND_INCLUDE_CAPABILITIES
-                            | Context.BIND_BYPASS_USER_NETWORK_RESTRICTIONS)) {
-                // The service host app doesn't get any capabilities from the client.
-                return false;
-            }
+        if (app.getSetAdj() >= client.getSetAdj()) {
+            return true;
+        } else if (app.getSetProcState() >= client.getSetProcState()) {
+            return true;
+        } else if (cr.hasFlag(Context.BIND_INCLUDE_CAPABILITIES
+                            | Context.BIND_BYPASS_USER_NETWORK_RESTRICTIONS)
+                && (app.getSetCapability() & client.getSetCapability())
+                            != PROCESS_CAPABILITY_NONE) {
+            return true;
+        } else if (Flags.unfreezeBindPolicyFix()
+                && cr.hasFlag(Context.BIND_WAIVE_PRIORITY
+                            | Context.BIND_ALLOW_OOM_MANAGEMENT)) {
+            return true;
+        } else if (Flags.unfreezeBindPolicyFix()
+                && app.mOptRecord.shouldNotFreeze()
+                && client.mOptRecord.shouldNotFreeze()) {
+            // Process has shouldNotFreeze and it could have gotten it from the client.
+            return true;
         }
-        return true;
+        return false;
     }
 
     @GuardedBy("mService")
@@ -4114,14 +4148,25 @@
         if (evaluateConnectionPrelude(client, app)) {
             return true;
         }
-        if (app.getSetAdj() <= client.getSetAdj()
-                && app.getSetProcState() <= client.getSetProcState()) {
-            // The provider host process has better states than the client, so no change.
-            return false;
+
+        boolean needDryRun = false;
+        if (app.getSetAdj() > client.getSetAdj()) {
+            needDryRun = true;
+        } else if (app.getSetProcState() > client.getSetProcState()) {
+            needDryRun = true;
+        } else if (Flags.unfreezeBindPolicyFix()
+                && client.mOptRecord.shouldNotFreeze()
+                && !app.mOptRecord.shouldNotFreeze()) {
+            needDryRun = true;
         }
-        return computeProviderHostOomAdjLSP(null, app, client, mInjector.getUptimeMillis(),
-                mService.getTopApp(), false, false, false, OOM_ADJ_REASON_NONE, CACHED_APP_MIN_ADJ,
-                false, true /* dryRun */);
+
+        if (needDryRun) {
+            return computeProviderHostOomAdjLSP(null, app, client, mInjector.getUptimeMillis(),
+                    mService.getTopApp(), false, false, false, OOM_ADJ_REASON_NONE,
+                    CACHED_APP_MIN_ADJ,
+                    false, true /* dryRun */);
+        }
+        return false;
     }
 
     @GuardedBy("mService")
@@ -4129,12 +4174,19 @@
         if (evaluateConnectionPrelude(client, app)) {
             return true;
         }
-        if (app.getSetAdj() < client.getSetAdj()
-                && app.getSetProcState() < client.getSetProcState()) {
-            // The provider host process has better states than the client, so no change.
-            return false;
+
+        if (app.getSetAdj() >= client.getSetAdj()) {
+            return true;
+        } else if (app.getSetProcState() >= client.getSetProcState()) {
+            return true;
+        } else if (Flags.unfreezeBindPolicyFix()
+                && app.mOptRecord.shouldNotFreeze()
+                && client.mOptRecord.shouldNotFreeze()) {
+            // Process has shouldNotFreeze and it could have gotten it from the client.
+            return true;
         }
-        return true;
+
+        return false;
     }
 
     private boolean evaluateConnectionPrelude(ProcessRecord client, ProcessRecord app) {
diff --git a/services/core/java/com/android/server/am/PendingIntentController.java b/services/core/java/com/android/server/am/PendingIntentController.java
index 3b0147c..97aa947 100644
--- a/services/core/java/com/android/server/am/PendingIntentController.java
+++ b/services/core/java/com/android/server/am/PendingIntentController.java
@@ -157,7 +157,8 @@
 
             PendingIntentRecord.Key key = new PendingIntentRecord.Key(type, packageName, featureId,
                     token, resultWho, requestCode, intents, resolvedTypes, flags,
-                    new SafeActivityOptions(opts), userId);
+                    new SafeActivityOptions(opts, Binder.getCallingPid(), Binder.getCallingUid()),
+                    userId);
             WeakReference<PendingIntentRecord> ref;
             ref = mIntentSenderRecords.get(key);
             PendingIntentRecord rec = ref != null ? ref.get() : null;
diff --git a/services/core/java/com/android/server/am/PendingIntentRecord.java b/services/core/java/com/android/server/am/PendingIntentRecord.java
index 3fb06a7..3817ba1 100644
--- a/services/core/java/com/android/server/am/PendingIntentRecord.java
+++ b/services/core/java/com/android/server/am/PendingIntentRecord.java
@@ -533,9 +533,9 @@
             // Extract options before clearing calling identity
             mergedOptions = key.options;
             if (mergedOptions == null) {
-                mergedOptions = new SafeActivityOptions(opts);
+                mergedOptions = new SafeActivityOptions(opts, callingPid, callingUid);
             } else {
-                mergedOptions.setCallerOptions(opts);
+                mergedOptions.setCallerOptions(opts, callingPid, callingUid);
             }
 
             if (mAllowlistDuration != null) {
@@ -629,7 +629,8 @@
                                     allIntents, allResolvedTypes, resultTo, mergedOptions, userId,
                                     false /* validateIncomingUser */,
                                     this /* originatingPendingIntent */,
-                                    getBackgroundStartPrivilegesForActivitySender(allowlistToken));
+                                    getBackgroundStartPrivilegesForActivitySender(allowlistToken)
+                                            .allowsBackgroundActivityStarts());
                         } else {
                             res = controller.mAtmInternal.startActivityInPackage(uid, callingPid,
                                     callingUid, key.packageName, key.featureId, finalIntent,
@@ -637,7 +638,8 @@
                                     mergedOptions, userId, null, "PendingIntentRecord",
                                     false /* validateIncomingUser */,
                                     this /* originatingPendingIntent */,
-                                    getBackgroundStartPrivilegesForActivitySender(allowlistToken));
+                                    getBackgroundStartPrivilegesForActivitySender(allowlistToken)
+                                            .allowsBackgroundActivityStarts());
                         }
                     } catch (RuntimeException e) {
                         Slog.w(TAG, "Unable to send startActivity intent", e);
diff --git a/services/core/java/com/android/server/am/PhantomProcessList.java b/services/core/java/com/android/server/am/PhantomProcessList.java
index 2ec1aed..bfdced7 100644
--- a/services/core/java/com/android/server/am/PhantomProcessList.java
+++ b/services/core/java/com/android/server/am/PhantomProcessList.java
@@ -514,6 +514,15 @@
         app.killLocked("Caused by child process: " + msg, reasonCode, subReason, true);
     }
 
+    @GuardedBy("mLock")
+    private SparseArray<PhantomProcessRecord> getPhantomProcessOfAppLocked(ProcessRecord app) {
+        int index = mAppPhantomProcessMap.indexOfKey(app.getPid());
+        if (index >= 0) {
+            return mAppPhantomProcessMap.valueAt(index);
+        }
+        return null;
+    }
+
     /**
      * Iterate all phantom process belonging to the given app, and invokve callback
      * for each of them.
@@ -521,20 +530,35 @@
     void forEachPhantomProcessOfApp(final ProcessRecord app,
             final Function<PhantomProcessRecord, Boolean> callback) {
         synchronized (mLock) {
-            int index = mAppPhantomProcessMap.indexOfKey(app.getPid());
-            if (index >= 0) {
-                final SparseArray<PhantomProcessRecord> array =
-                        mAppPhantomProcessMap.valueAt(index);
-                for (int i = array.size() - 1; i >= 0; i--) {
-                    final PhantomProcessRecord r = array.valueAt(i);
-                    if (!callback.apply(r)) {
-                        break;
-                    }
+            final SparseArray<PhantomProcessRecord> array = getPhantomProcessOfAppLocked(app);
+            if (array == null) {
+                return;
+            }
+            for (int i = array.size() - 1; i >= 0; i--) {
+                final PhantomProcessRecord r = array.valueAt(i);
+                if (!callback.apply(r)) {
+                    break;
                 }
             }
         }
     }
 
+    /**
+     * Set process group of phantom process belonging to the given app.
+     */
+    void setProcessGroupForPhantomProcessOfApp(final ProcessRecord app, final int group) {
+        synchronized (mLock) {
+            final SparseArray<PhantomProcessRecord> array = getPhantomProcessOfAppLocked(app);
+            if (array == null) {
+                return;
+            }
+            for (int i = array.size() - 1; i >= 0; i--) {
+                final PhantomProcessRecord r = array.valueAt(i);
+                mService.mOomAdjuster.setProcessGroup(r.mPid, group, r.mProcessName);
+            }
+        }
+    }
+
     @GuardedBy("tracker")
     void updateProcessCpuStatesLocked(ProcessCpuTracker tracker) {
         synchronized (mLock) {
diff --git a/services/core/java/com/android/server/am/ProcessCachedOptimizerRecord.java b/services/core/java/com/android/server/am/ProcessCachedOptimizerRecord.java
index 57a5e3f..980c1f5 100644
--- a/services/core/java/com/android/server/am/ProcessCachedOptimizerRecord.java
+++ b/services/core/java/com/android/server/am/ProcessCachedOptimizerRecord.java
@@ -18,8 +18,8 @@
 
 import android.annotation.IntDef;
 import android.annotation.UptimeMillisLong;
-import android.app.ActivityManagerInternal.OomAdjReason;
 import android.app.ActivityManagerInternal.FrozenProcessListener;
+import android.app.ActivityManagerInternal.OomAdjReason;
 import android.util.Pair;
 import android.util.TimeUtils;
 
@@ -338,7 +338,11 @@
     boolean setShouldNotFreeze(boolean shouldNotFreeze, boolean dryRun,
             @ShouldNotFreezeReason int reason, int adjSeq) {
         if (dryRun) {
-            return mFrozen && !shouldNotFreeze;
+            if (Flags.unfreezeBindPolicyFix()) {
+                return mShouldNotFreeze != shouldNotFreeze;
+            } else {
+                return mFrozen && !shouldNotFreeze;
+            }
         }
         if (Flags.traceUpdateAppFreezeStateLsp()) {
             if (shouldNotFreeze) {
diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
index 7afcb13..7cfe829 100644
--- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
+++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
@@ -176,6 +176,7 @@
         "core_libraries",
         "crumpet",
         "dck_framework",
+        "desktop_hwsec",
         "desktop_stats",
         "devoptions_settings",
         "game",
@@ -192,6 +193,7 @@
         "make_pixel_haptics",
         "media_audio",
         "media_drm",
+        "media_projection",
         "media_reliability",
         "media_solutions",
         "media_tv",
@@ -204,6 +206,7 @@
         "pixel_bluetooth",
         "pixel_connectivity_gps",
         "pixel_continuity",
+        "pixel_perf",
         "pixel_sensors",
         "pixel_system_sw_video",
         "pixel_watch",
@@ -232,6 +235,7 @@
         "text",
         "threadnetwork",
         "treble",
+        "tv_os_media",
         "tv_system_ui",
         "usb",
         "vibrator",
diff --git a/services/core/java/com/android/server/am/flags.aconfig b/services/core/java/com/android/server/am/flags.aconfig
index d67fea0..8834820 100644
--- a/services/core/java/com/android/server/am/flags.aconfig
+++ b/services/core/java/com/android/server/am/flags.aconfig
@@ -233,3 +233,21 @@
     description: "Assign cached oom_score_adj in tiers."
     bug: "369893532"
 }
+
+flag {
+    name: "restrict_priority_values"
+    namespace: "backstage_power"
+    description: "Restrict priority values defined by non-system apps"
+    is_fixed_read_only: true
+    bug: "369487976"
+}
+
+flag {
+    name: "unfreeze_bind_policy_fix"
+    namespace: "backstage_power"
+    description: "Make sure shouldNotFreeze state change correctly triggers updates."
+    bug: "375691778"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index dbdc614..906e584 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -2641,8 +2641,8 @@
                 Log.w(TAG, "failed to broadcast ACTION_SPEAKERPHONE_STATE_CHANGED: " + e);
             }
         }
-        mAudioService.postUpdateRingerModeServiceInt();
         dispatchCommunicationDevice();
+        mAudioService.postUpdateRingerModeServiceInt();
     }
 
     @GuardedBy("mDeviceStateLock")
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index bfef685..3dbe48a 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -1608,7 +1608,14 @@
                 if (dev == AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP) {
                     enabled = mAvrcpAbsVolSupported;
                 }
-                mAudioSystem.setDeviceAbsoluteVolumeEnabled(dev, /*address=*/"", enabled, stream);
+                final int result = mAudioSystem.setDeviceAbsoluteVolumeEnabled(dev, /*address=*/"",
+                        enabled, stream);
+                if (result != AudioSystem.AUDIO_STATUS_OK) {
+                    sVolumeLogger.enqueueAndSlog(
+                            new VolumeEvent(VolumeEvent.VOL_ABS_DEVICE_ENABLED_ERROR,
+                                    result, dev, enabled, stream).eventToString(), ALOGE, TAG);
+
+                }
             });
         }
     }
@@ -2063,16 +2070,6 @@
 
         onIndicateSystemReady();
 
-        synchronized (mCachedAbsVolDrivingStreamsLock) {
-            mCachedAbsVolDrivingStreams.forEach((dev, stream) -> {
-                boolean enabled = true;
-                if (dev == AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP) {
-                    enabled = mAvrcpAbsVolSupported;
-                }
-                mAudioSystem.setDeviceAbsoluteVolumeEnabled(dev, /*address=*/"", enabled, stream);
-            });
-        }
-
         // indicate the end of reconfiguration phase to audio HAL
         AudioSystem.setParameters("restarting=false");
 
@@ -2187,6 +2184,22 @@
             return;
         }
 
+        synchronized (mCachedAbsVolDrivingStreamsLock) {
+            mCachedAbsVolDrivingStreams.forEach((dev, stream) -> {
+                boolean enabled = true;
+                if (dev == AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP) {
+                    enabled = mAvrcpAbsVolSupported;
+                }
+                final int result = mAudioSystem.setDeviceAbsoluteVolumeEnabled(dev, /*address=*/"",
+                        enabled, stream);
+                if (result != AudioSystem.AUDIO_STATUS_OK) {
+                    sVolumeLogger.enqueueAndSlog(
+                            new VolumeEvent(VolumeEvent.VOL_ABS_DEVICE_ENABLED_ERROR,
+                                    result, dev, enabled, stream).eventToString(), ALOGE, TAG);
+                }
+            });
+        }
+
         // did it work? check based on min/max values of some basic streams
         if (!checkVolumeRangeInitialization(caller)) {
             return;
@@ -4917,15 +4930,27 @@
     private void onUpdateContextualVolumes() {
         final int streamType = getBluetoothContextualVolumeStream();
 
+        Log.i(TAG,
+                "onUpdateContextualVolumes: absolute volume driving streams " + streamType
+                + " avrcp supported: " + mAvrcpAbsVolSupported);
         synchronized (mCachedAbsVolDrivingStreamsLock) {
             mCachedAbsVolDrivingStreams.replaceAll((absDev, stream) -> {
                 boolean enabled = true;
                 if (absDev == AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP) {
                     enabled = mAvrcpAbsVolSupported;
+                    if (!enabled) {
+                        Log.w(TAG, "Updating avrcp not supported in onUpdateContextualVolumes");
+                    }
                 }
-                if (stream != streamType || !enabled) {
-                    mAudioSystem.setDeviceAbsoluteVolumeEnabled(absDev, /*address=*/"",
-                            enabled, streamType);
+                if (stream != streamType) {
+                    final int result = mAudioSystem.setDeviceAbsoluteVolumeEnabled(absDev,
+                            /*address=*/"", enabled, streamType);
+                    if (result != AudioSystem.AUDIO_STATUS_OK) {
+                        sVolumeLogger.enqueueAndSlog(
+                                new VolumeEvent(VolumeEvent.VOL_ABS_DEVICE_ENABLED_ERROR,
+                                        result, absDev, enabled, streamType).eventToString(), ALOGE,
+                                TAG);
+                    }
                 }
                 return streamType;
             });
@@ -10563,14 +10588,23 @@
     }
 
     /*package*/ void setAvrcpAbsoluteVolumeSupported(boolean support) {
+        Log.i(TAG, "setAvrcpAbsoluteVolumeSupported support " + support);
         synchronized (mCachedAbsVolDrivingStreamsLock) {
             mAvrcpAbsVolSupported = support;
             if (absVolumeIndexFix()) {
                 int a2dpDev = AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP;
                 mCachedAbsVolDrivingStreams.compute(a2dpDev, (dev, stream) -> {
                     if (!mAvrcpAbsVolSupported) {
-                        mAudioSystem.setDeviceAbsoluteVolumeEnabled(a2dpDev, /*address=*/
-                                "", /*enabled*/false, AudioSystem.STREAM_DEFAULT);
+                        final int result = mAudioSystem.setDeviceAbsoluteVolumeEnabled(
+                                a2dpDev, /*address=*/"", /*enabled*/false,
+                                AudioSystem.STREAM_DEFAULT);
+                        if (result != AudioSystem.AUDIO_STATUS_OK) {
+                            sVolumeLogger.enqueueAndSlog(
+                                    new VolumeEvent(VolumeEvent.VOL_ABS_DEVICE_ENABLED_ERROR,
+                                            result, a2dpDev, /*enabled=*/false,
+                                            AudioSystem.STREAM_DEFAULT).eventToString(), ALOGE,
+                                    TAG);
+                        }
                         return null;
                     }
                     // For A2DP and AVRCP we need to set the driving stream based on the
@@ -10578,8 +10612,14 @@
                     // and setStreamVolume that the driving abs volume stream is consistent.
                     int streamToDriveAbs = getBluetoothContextualVolumeStream();
                     if (stream == null || stream != streamToDriveAbs) {
-                        mAudioSystem.setDeviceAbsoluteVolumeEnabled(a2dpDev, /*address=*/
-                                "", /*enabled*/true, streamToDriveAbs);
+                        final int result = mAudioSystem.setDeviceAbsoluteVolumeEnabled(a2dpDev,
+                                /*address=*/"", /*enabled*/true, streamToDriveAbs);
+                        if (result != AudioSystem.AUDIO_STATUS_OK) {
+                            sVolumeLogger.enqueueAndSlog(
+                                    new VolumeEvent(VolumeEvent.VOL_ABS_DEVICE_ENABLED_ERROR,
+                                            result, a2dpDev, /*enabled=*/true,
+                                            streamToDriveAbs).eventToString(), ALOGE, TAG);
+                        }
                     }
                     return streamToDriveAbs;
                 });
@@ -12679,7 +12719,7 @@
         pw.println("\nLoudness alignment:");
         mLoudnessCodecHelper.dump(pw);
 
-        pw.println("\nAbsolute voume devices:");
+        pw.println("\nAbsolute volume devices with their volume driving streams:");
         synchronized (mCachedAbsVolDrivingStreamsLock) {
             mCachedAbsVolDrivingStreams.forEach((dev, stream) -> pw.println(
                     "Device type: 0x" + Integer.toHexString(dev) + ", driving stream " + stream));
diff --git a/services/core/java/com/android/server/audio/AudioServiceEvents.java b/services/core/java/com/android/server/audio/AudioServiceEvents.java
index 631d5d8..995e16b 100644
--- a/services/core/java/com/android/server/audio/AudioServiceEvents.java
+++ b/services/core/java/com/android/server/audio/AudioServiceEvents.java
@@ -234,6 +234,7 @@
         static final int VOL_SET_LE_AUDIO_VOL = 10;
         static final int VOL_ADJUST_GROUP_VOL = 11;
         static final int VOL_MASTER_MUTE = 12;
+        static final int VOL_ABS_DEVICE_ENABLED_ERROR = 13;
 
         final int mOp;
         final int mStream;
@@ -365,6 +366,19 @@
             logMetricEvent();
         }
 
+        /** used for VOL_ABS_DEVICE_ENABLED_ERROR */
+        VolumeEvent(int op, int result, int device, boolean enabled, int streamType) {
+            mOp = op;
+            mStream = streamType;
+            mVal1 = device;
+            mVal2 = enabled ? 1 : 0;
+            mVal3 = result;
+            // unused
+            mCaller = null;
+            mGroupName = null;
+            logMetricEvent();
+        }
+
 
         /**
          * Audio Analytics unique Id.
@@ -481,6 +495,9 @@
                 case VOL_MASTER_MUTE:
                     // No value in logging metrics for this internal event
                     return;
+                case VOL_ABS_DEVICE_ENABLED_ERROR:
+                    // No value in logging metrics for this internal event
+                    return;
                 default:
                     return;
             }
@@ -569,6 +586,13 @@
                     return new StringBuilder("Master mute:")
                             .append(mVal1 == 1 ? " muted)" : " unmuted)")
                             .toString();
+                case VOL_ABS_DEVICE_ENABLED_ERROR:
+                    return new StringBuilder("setDeviceAbsoluteVolumeEnabled failed with ")
+                            .append(mVal3)
+                            .append(" for dev: 0x").append(Integer.toHexString(mVal1))
+                            .append(" enabled: ").append(mVal2)
+                            .append(" streamType: ").append(mStream)
+                            .toString();
                 default: return new StringBuilder("FIXME invalid op:").append(mOp).toString();
             }
         }
diff --git a/services/core/java/com/android/server/audio/FocusRequester.java b/services/core/java/com/android/server/audio/FocusRequester.java
index f462539..5ebe6a1 100644
--- a/services/core/java/com/android/server/audio/FocusRequester.java
+++ b/services/core/java/com/android/server/audio/FocusRequester.java
@@ -16,6 +16,9 @@
 
 package com.android.server.audio;
 
+import static com.android.server.utils.EventLogger.Event.ALOGE;
+import static com.android.server.utils.EventLogger.Event.ALOGW;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.pm.UserProperties;
@@ -24,6 +27,7 @@
 import android.media.AudioManager;
 import android.media.IAudioFocusDispatcher;
 import android.os.IBinder;
+import android.os.RemoteException;
 import android.os.UserHandle;
 import android.util.Log;
 
@@ -31,6 +35,7 @@
 import com.android.server.LocalServices;
 import com.android.server.audio.MediaFocusControl.AudioFocusDeathHandler;
 import com.android.server.pm.UserManagerInternal;
+import com.android.server.utils.EventLogger;
 
 import java.io.PrintWriter;
 import java.util.List;
@@ -84,6 +89,8 @@
      */
     private final @NonNull AudioAttributes mAttributes;
 
+    private final EventLogger mEventLogger;
+
     /**
      * Class constructor
      * @param aa
@@ -100,7 +107,7 @@
     FocusRequester(@NonNull AudioAttributes aa, int focusRequest, int grantFlags,
             IAudioFocusDispatcher afl, IBinder source, @NonNull String id,
             AudioFocusDeathHandler hdlr, @NonNull String pn, int uid,
-            @NonNull MediaFocusControl ctlr, int sdk) {
+            @NonNull MediaFocusControl ctlr, int sdk, EventLogger eventLogger) {
         mAttributes = aa;
         mFocusDispatcher = afl;
         mSourceRef = source;
@@ -115,10 +122,12 @@
         mFocusLossFadeLimbo = false;
         mFocusController = ctlr;
         mSdkTarget = sdk;
+        mEventLogger = eventLogger;
     }
 
     FocusRequester(AudioFocusInfo afi, IAudioFocusDispatcher afl,
-             IBinder source, AudioFocusDeathHandler hdlr, @NonNull MediaFocusControl ctlr) {
+             IBinder source, AudioFocusDeathHandler hdlr, @NonNull MediaFocusControl ctlr,
+             EventLogger eventLogger) {
         mAttributes = afi.getAttributes();
         mClientId = afi.getClientId();
         mPackageName = afi.getPackageName();
@@ -134,6 +143,7 @@
         mSourceRef = source;
         mDeathHandler = hdlr;
         mFocusController = ctlr;
+        mEventLogger = eventLogger;
     }
 
     boolean hasSameClient(String otherClient) {
@@ -357,18 +367,22 @@
             mFocusController.notifyExtPolicyFocusGrant_syncAf(toAudioFocusInfo(),
                     AudioManager.AUDIOFOCUS_REQUEST_GRANTED);
             final IAudioFocusDispatcher fd = mFocusDispatcher;
-            if (fd != null) {
+            if (fd != null && mFocusLossWasNotified) {
                 if (DEBUG) {
                     Log.v(TAG, "dispatching " + focusChangeToString(focusGain) + " to "
                         + mClientId);
                 }
-                if (mFocusLossWasNotified) {
-                    fd.dispatchAudioFocusChange(focusGain, mClientId);
-                }
+                fd.dispatchAudioFocusChange(focusGain, mClientId);
+                mEventLogger.enqueue(new FocusRequestEvent(
+                        this, focusGain, "handleGain"));
+            } else if (mFocusLossWasNotified) {
+                mEventLogger.enqueue(new FocusRequestEvent(
+                        this, focusGain, "handleGain no listener").printSlog(ALOGW, TAG));
             }
             mFocusController.restoreVShapedPlayers(this);
-        } catch (android.os.RemoteException e) {
-            Log.e(TAG, "Failure to signal gain of audio focus due to: ", e);
+        } catch (RemoteException e) {
+            mEventLogger.enqueue(new FocusRequestEvent(
+                    this, focusGain, "handleGain exc: " + e).printSlog(ALOGE, TAG));
         }
     }
 
@@ -385,62 +399,67 @@
         if (DEBUG) {
             Log.i(TAG, "handleFocusLoss for " + mClientId + " loss:" + focusLoss);
         }
-        try {
-            if (focusLoss != mFocusLossReceived) {
-                mFocusLossReceived = focusLoss;
-                mFocusLossWasNotified = false;
-                // before dispatching a focus loss, check if the following conditions are met:
-                // 1/ the framework is not supposed to notify the focus loser on a DUCK loss
-                //    (i.e. it has a focus controller that implements a ducking policy)
-                // 2/ it is a DUCK loss
-                // 3/ the focus loser isn't flagged as pausing in a DUCK loss
-                // if they are, do not notify the focus loser
-                if (!mFocusController.mustNotifyFocusOwnerOnDuck()
-                        && mFocusLossReceived == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK
-                        && (mGrantFlags
-                                & AudioManager.AUDIOFOCUS_FLAG_PAUSES_ON_DUCKABLE_LOSS) == 0) {
-                    if (DEBUG) {
-                        Log.v(TAG, "NOT dispatching " + focusChangeToString(mFocusLossReceived)
-                                + " to " + mClientId + ", to be handled externally");
-                    }
-                    mFocusController.notifyExtPolicyFocusLoss_syncAf(
-                            toAudioFocusInfo(), false /* wasDispatched */);
-                    return;
+        if (focusLoss != mFocusLossReceived) {
+            mFocusLossReceived = focusLoss;
+            mFocusLossWasNotified = false;
+            // before dispatching a focus loss, check if the following conditions are met:
+            // 1/ the framework is not supposed to notify the focus loser on a DUCK loss
+            //    (i.e. it has a focus controller that implements a ducking policy)
+            // 2/ it is a DUCK loss
+            // 3/ the focus loser isn't flagged as pausing in a DUCK loss
+            // if they are, do not notify the focus loser
+            if (!mFocusController.mustNotifyFocusOwnerOnDuck()
+                    && mFocusLossReceived == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK
+                    && (mGrantFlags
+                            & AudioManager.AUDIOFOCUS_FLAG_PAUSES_ON_DUCKABLE_LOSS) == 0) {
+                if (DEBUG) {
+                    Log.v(TAG, "NOT dispatching " + focusChangeToString(mFocusLossReceived)
+                            + " to " + mClientId + ", to be handled externally");
                 }
-
-                // check enforcement by the framework
-                boolean handled = false;
-                if (frWinner != null) {
-                    handled = frameworkHandleFocusLoss(focusLoss, frWinner, forceDuck);
-                }
-
-                if (handled) {
-                    if (DEBUG) {
-                        Log.v(TAG, "NOT dispatching " + focusChangeToString(mFocusLossReceived)
-                                + " to " + mClientId + ", response handled by framework");
-                    }
-                    mFocusController.notifyExtPolicyFocusLoss_syncAf(
-                            toAudioFocusInfo(), false /* wasDispatched */);
-                    return; // with mFocusLossWasNotified = false
-                }
-
-                final IAudioFocusDispatcher fd = mFocusDispatcher;
-                if (fd != null) {
-                    if (DEBUG) {
-                        Log.v(TAG, "dispatching " + focusChangeToString(mFocusLossReceived) + " to "
-                            + mClientId);
-                    }
-                    mFocusController.notifyExtPolicyFocusLoss_syncAf(
-                            toAudioFocusInfo(), true /* wasDispatched */);
-                    mFocusLossWasNotified = true;
-                    fd.dispatchAudioFocusChange(mFocusLossReceived, mClientId);
-                } else if (DEBUG) {
-                    Log.i(TAG, "NOT dispatching " + focusChangeToString(mFocusLossReceived)
-                            + " to " + mClientId + " no IAudioFocusDispatcher");
-                }
+                mFocusController.notifyExtPolicyFocusLoss_syncAf(
+                        toAudioFocusInfo(), false /* wasDispatched */);
+                return;
             }
-        } catch (android.os.RemoteException e) {
-            Log.e(TAG, "Failure to signal loss of audio focus due to:", e);
+
+            // check enforcement by the framework
+            boolean handled = false;
+            if (frWinner != null) {
+                handled = frameworkHandleFocusLoss(focusLoss, frWinner, forceDuck);
+            }
+
+            if (handled) {
+                if (DEBUG) {
+                    Log.v(TAG, "NOT dispatching " + focusChangeToString(mFocusLossReceived)
+                            + " to " + mClientId + ", response handled by framework");
+                }
+                mFocusController.notifyExtPolicyFocusLoss_syncAf(
+                        toAudioFocusInfo(), false /* wasDispatched */);
+                return; // with mFocusLossWasNotified = false
+            }
+
+            final IAudioFocusDispatcher fd = mFocusDispatcher;
+            if (fd != null) {
+                if (DEBUG) {
+                    Log.v(TAG, "dispatching " + focusChangeToString(mFocusLossReceived) + " to "
+                        + mClientId);
+                }
+                mFocusController.notifyExtPolicyFocusLoss_syncAf(
+                        toAudioFocusInfo(), true /* wasDispatched */);
+                mFocusLossWasNotified = true;
+                try {
+                    fd.dispatchAudioFocusChange(mFocusLossReceived, mClientId);
+                    mEventLogger.enqueue(new FocusRequestEvent(
+                                this, mFocusLossReceived, "handleLoss"));
+                } catch (RemoteException e) {
+                    mEventLogger.enqueue(new FocusRequestEvent(
+                                this, mFocusLossReceived, "handleLoss failed exc: " + e)
+                            .printSlog(ALOGE,TAG));
+                }
+            } else {
+                mEventLogger.enqueue(new FocusRequestEvent(
+                            this, mFocusLossReceived, "handleLoss failed no listener")
+                        .printSlog(ALOGE, TAG));
+            }
         }
     }
 
@@ -505,7 +524,7 @@
         return false;
     }
 
-    int dispatchFocusChange(int focusChange) {
+    int dispatchFocusChange(int focusChange, String reason) {
         final IAudioFocusDispatcher fd = mFocusDispatcher;
         if (fd == null) {
             if (MediaFocusControl.DEBUG) { Log.e(TAG, "dispatchFocusChange: no focus dispatcher"); }
@@ -528,8 +547,11 @@
         }
         try {
             fd.dispatchAudioFocusChange(focusChange, mClientId);
-        } catch (android.os.RemoteException e) {
-            Log.e(TAG, "dispatchFocusChange: error talking to focus listener " + mClientId, e);
+            mEventLogger.enqueue(new FocusRequestEvent(this,
+                        focusChange, "dispatch: "  + reason));
+        } catch (RemoteException e) {
+            mEventLogger.enqueue(new FocusRequestEvent(
+                        this, focusChange, "dispatch failed: " + e).printSlog(ALOGE, TAG));
             return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
         }
         return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
@@ -559,7 +581,7 @@
                 }
             }
         }
-        return dispatchFocusChange(focusChange);
+        return dispatchFocusChange(focusChange, "focus with fade");
     }
 
     void dispatchFocusResultFromExtPolicy(int requestResult) {
@@ -575,7 +597,7 @@
         }
         try {
             fd.dispatchFocusResultFromExtPolicy(requestResult, mClientId);
-        } catch (android.os.RemoteException e) {
+        } catch (RemoteException e) {
             Log.e(TAG, "dispatchFocusResultFromExtPolicy: error talking to focus listener"
                     + mClientId, e);
         }
@@ -585,4 +607,32 @@
         return new AudioFocusInfo(mAttributes, mCallingUid, mClientId, mPackageName,
                 mFocusGainRequest, mFocusLossReceived, mGrantFlags, mSdkTarget);
     }
+
+    static class FocusRequestEvent extends EventLogger.Event {
+        private final String mClientId;
+        private final int mUid;
+        private final String  mPackageName;
+        private final int mCode;
+        private final String mDescription;
+
+        public FocusRequestEvent(FocusRequester fr, String description) {
+            this(fr, -1, description);
+        }
+
+        public FocusRequestEvent(FocusRequester fr, int code, String description) {
+            mClientId = fr.getClientId();
+            mUid = fr.getClientUid();
+            mPackageName = fr.getPackageName();
+            mCode = code;
+            mDescription = description != null ? description : "";
+        }
+        @Override
+        public String eventToString() {
+            return "focus owner: " + mClientId + " in uid: " + mUid
+                + " pack: " + mPackageName
+                + ((mCode != -1) ? " code: " + mCode : "")
+                + " event: " + mDescription;
+        }
+    }
+
 }
diff --git a/services/core/java/com/android/server/audio/MediaFocusControl.java b/services/core/java/com/android/server/audio/MediaFocusControl.java
index b4af46e..1da62d7 100644
--- a/services/core/java/com/android/server/audio/MediaFocusControl.java
+++ b/services/core/java/com/android/server/audio/MediaFocusControl.java
@@ -231,13 +231,8 @@
                 final FocusRequester focusOwner = stackIterator.next();
                 if (focusOwner.hasSameUid(uid) && focusOwner.hasSamePackage(packageName)) {
                     clientsToRemove.add(focusOwner.getClientId());
-                    mEventLogger.enqueue((new EventLogger.StringEvent(
-                            "focus owner:" + focusOwner.getClientId()
-                                    + " in uid:" + uid + " pack: " + packageName
-                                    + " getting AUDIOFOCUS_LOSS due to app suspension"))
-                            .printLog(TAG));
                     // make the suspended app lose focus through its focus listener (if any)
-                    focusOwner.dispatchFocusChange(AudioManager.AUDIOFOCUS_LOSS);
+                    focusOwner.dispatchFocusChange(AudioManager.AUDIOFOCUS_LOSS, "app suspension");
                 }
             }
             for (String clientToRemove : clientsToRemove) {
@@ -548,11 +543,7 @@
             FocusRequester fr = stackIterator.next();
             if(fr.hasSameBinder(cb)) {
                 Log.i(TAG, "AudioFocus  removeFocusStackEntryOnDeath(): removing entry for " + cb);
-                mEventLogger.enqueue(new EventLogger.StringEvent(
-                        "focus requester:" + fr.getClientId()
-                                + " in uid:" + fr.getClientUid()
-                                + " pack:" + fr.getPackageName()
-                                + " died"));
+                mEventLogger.enqueue(new FocusRequester.FocusRequestEvent(fr,  " died"));
                 notifyExtPolicyFocusLoss_syncAf(fr.toAudioFocusInfo(), false);
 
                 stackIterator.remove();
@@ -585,11 +576,7 @@
             final FocusRequester fr = owner.getValue();
             if (fr.hasSameBinder(cb)) {
                 ownerIterator.remove();
-                mEventLogger.enqueue(new EventLogger.StringEvent(
-                        "focus requester:" + fr.getClientId()
-                                + " in uid:" + fr.getClientUid()
-                                + " pack:" + fr.getPackageName()
-                                + " died"));
+                mEventLogger.enqueue(new FocusRequester.FocusRequestEvent(fr, "died"));
                 fr.release();
                 notifyExtFocusPolicyFocusAbandon_syncAf(fr.toAudioFocusInfo());
                 break;
@@ -900,7 +887,7 @@
             }
             // new focus (future) focus owner to keep track of
             mFocusOwnersForFocusPolicy.put(afi.getClientId(),
-                    new FocusRequester(afi, fd, cb, hdlr, this));
+                    new FocusRequester(afi, fd, cb, hdlr, this, mEventLogger));
         }
 
         try {
@@ -972,7 +959,7 @@
                 }
                 return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
             }
-            return fr.dispatchFocusChange(focusChange);
+            return fr.dispatchFocusChange(focusChange, "audiomanager");
         }
     }
 
@@ -1006,6 +993,7 @@
                 otherActiveFrs.add(otherFr);
             }
 
+            // TODO log
             int status = fr.dispatchFocusChangeWithFadeLocked(focusChange, otherActiveFrs);
             if (status != AudioManager.AUDIOFOCUS_REQUEST_DELAYED
                     && focusChange == AudioManager.AUDIOFOCUS_LOSS) {
@@ -1260,7 +1248,7 @@
             removeFocusStackEntry(clientId, false /* signal */, false /*notifyFocusFollowers*/);
 
             final FocusRequester nfr = new FocusRequester(aa, focusChangeHint, flags, fd, cb,
-                    clientId, afdh, callingPackageName, uid, this, sdk);
+                    clientId, afdh, callingPackageName, uid, this, sdk, mEventLogger);
 
             if (mMultiAudioFocusEnabled
                     && (focusChangeHint == AudioManager.AUDIOFOCUS_GAIN)) {
@@ -1594,7 +1582,7 @@
                         synchronized (mAudioFocusLock) {
                             final FocusRequester loser = (FocusRequester) msg.obj;
                             if (loser.isInFocusLossLimbo()) {
-                                loser.dispatchFocusChange(AudioManager.AUDIOFOCUS_LOSS);
+                                loser.dispatchFocusChange(AudioManager.AUDIOFOCUS_LOSS, "loss after fade");
                                 loser.release();
                                 postForgetUidLater(loser);
                             }
diff --git a/services/core/java/com/android/server/biometrics/PreAuthInfo.java b/services/core/java/com/android/server/biometrics/PreAuthInfo.java
index b2c616a..96c178a 100644
--- a/services/core/java/com/android/server/biometrics/PreAuthInfo.java
+++ b/services/core/java/com/android/server/biometrics/PreAuthInfo.java
@@ -112,7 +112,7 @@
             throws RemoteException {
 
         final boolean isOnlyMandatoryBiometricsRequested = promptInfo.getAuthenticators()
-                == BiometricManager.Authenticators.MANDATORY_BIOMETRICS;
+                == BiometricManager.Authenticators.IDENTITY_CHECK;
         boolean isMandatoryBiometricsAuthentication = false;
 
         if (dropCredentialFallback(promptInfo.getAuthenticators(),
@@ -180,8 +180,8 @@
     private static boolean dropCredentialFallback(int authenticators,
             boolean isMandatoryBiometricsEnabled, ITrustManager trustManager) {
         final boolean isMandatoryBiometricsRequested =
-                (authenticators & BiometricManager.Authenticators.MANDATORY_BIOMETRICS)
-                        == BiometricManager.Authenticators.MANDATORY_BIOMETRICS;
+                (authenticators & BiometricManager.Authenticators.IDENTITY_CHECK)
+                        == BiometricManager.Authenticators.IDENTITY_CHECK;
         if (Flags.mandatoryBiometrics() && isMandatoryBiometricsEnabled
                 && isMandatoryBiometricsRequested) {
             try {
diff --git a/services/core/java/com/android/server/biometrics/Utils.java b/services/core/java/com/android/server/biometrics/Utils.java
index 8734136..c1f8e2e 100644
--- a/services/core/java/com/android/server/biometrics/Utils.java
+++ b/services/core/java/com/android/server/biometrics/Utils.java
@@ -147,7 +147,7 @@
      * @return true if mandatory biometrics is requested
      */
     static boolean isMandatoryBiometricsRequested(@Authenticators.Types int authenticators) {
-        return (authenticators & Authenticators.MANDATORY_BIOMETRICS) != 0;
+        return (authenticators & Authenticators.IDENTITY_CHECK) != 0;
     }
 
     /**
@@ -257,7 +257,7 @@
         if (Flags.mandatoryBiometrics()) {
             testBits = ~(Authenticators.DEVICE_CREDENTIAL
                     | Authenticators.BIOMETRIC_MIN_STRENGTH
-                    | Authenticators.MANDATORY_BIOMETRICS);
+                    | Authenticators.IDENTITY_CHECK);
         } else {
             testBits = ~(Authenticators.DEVICE_CREDENTIAL
                     | Authenticators.BIOMETRIC_MIN_STRENGTH);
@@ -329,8 +329,8 @@
             case BiometricConstants.BIOMETRIC_ERROR_SENSOR_PRIVACY_ENABLED:
                 biometricManagerCode = BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE;
                 break;
-            case BiometricConstants.BIOMETRIC_ERROR_MANDATORY_NOT_ACTIVE:
-                biometricManagerCode = BiometricManager.BIOMETRIC_ERROR_MANDATORY_NOT_ACTIVE;
+            case BiometricConstants.BIOMETRIC_ERROR_IDENTITY_CHECK_NOT_ACTIVE:
+                biometricManagerCode = BiometricManager.BIOMETRIC_ERROR_IDENTITY_CHECK_NOT_ACTIVE;
                 break;
             case BiometricConstants.BIOMETRIC_ERROR_NOT_ENABLED_FOR_APPS:
                 biometricManagerCode = BiometricManager.BIOMETRIC_ERROR_NOT_ENABLED_FOR_APPS;
@@ -397,7 +397,7 @@
             case BIOMETRIC_SENSOR_PRIVACY_ENABLED:
                 return BiometricConstants.BIOMETRIC_ERROR_SENSOR_PRIVACY_ENABLED;
             case MANDATORY_BIOMETRIC_UNAVAILABLE_ERROR:
-                return BiometricConstants.BIOMETRIC_ERROR_MANDATORY_NOT_ACTIVE;
+                return BiometricConstants.BIOMETRIC_ERROR_IDENTITY_CHECK_NOT_ACTIVE;
             case BIOMETRIC_NOT_ENABLED_FOR_APPS:
                 if (Flags.mandatoryBiometrics()) {
                     return BiometricConstants.BIOMETRIC_ERROR_NOT_ENABLED_FOR_APPS;
diff --git a/services/core/java/com/android/server/biometrics/log/BiometricFrameworkStatsLogger.java b/services/core/java/com/android/server/biometrics/log/BiometricFrameworkStatsLogger.java
index 93b0e66..30c7319 100644
--- a/services/core/java/com/android/server/biometrics/log/BiometricFrameworkStatsLogger.java
+++ b/services/core/java/com/android/server/biometrics/log/BiometricFrameworkStatsLogger.java
@@ -115,7 +115,7 @@
     /** {@see FrameworkStatsLog.BIOMETRIC_ENROLLED}. */
     public void enroll(int statsModality, int statsAction, int statsClient,
             int targetUserId, long latency, boolean enrollSuccessful, float ambientLightLux,
-            int source) {
+            int source, int templateId) {
         FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_ENROLLED,
                 statsModality,
                 targetUserId,
@@ -124,7 +124,27 @@
                 -1, /* sensorId */
                 ambientLightLux,
                 source,
-                -1 /* templateId*/);
+                templateId);
+    }
+
+    /** {@see FrameworkStatsLog.BIOMETRIC_UNENROLLED} */
+    public void unenrolled(int statsModality, int targetUserId, int reason, int templateId) {
+        FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_UNENROLLED,
+                statsModality,
+                targetUserId,
+                reason,
+                templateId);
+    }
+
+    /** {@see FrameworkStatsLog.BIOMETRIC_ENUMERATED} */
+    public void enumerated(int statsModality, int targetUserId, int result, int[] templateIdsHal,
+            int[] templateIdsFramework) {
+        FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_ENUMERATED,
+                statsModality,
+                targetUserId,
+                result,
+                templateIdsHal,
+                templateIdsFramework);
     }
 
     /** {@see FrameworkStatsLog.BIOMETRIC_ERROR_OCCURRED}. */
diff --git a/services/core/java/com/android/server/biometrics/log/BiometricLogger.java b/services/core/java/com/android/server/biometrics/log/BiometricLogger.java
index 9351bc0..08582ca 100644
--- a/services/core/java/com/android/server/biometrics/log/BiometricLogger.java
+++ b/services/core/java/com/android/server/biometrics/log/BiometricLogger.java
@@ -32,6 +32,8 @@
 import com.android.server.biometrics.AuthenticationStatsCollector;
 import com.android.server.biometrics.Utils;
 
+import java.util.Arrays;
+
 /**
  * Logger for all reported Biometric framework events.
  */
@@ -254,7 +256,7 @@
 
     /** Log enrollment outcome. */
     public void logOnEnrolled(int targetUserId, long latency, boolean enrollSuccessful,
-            int source) {
+            int source, int templateId) {
         if (!mShouldLogMetrics) {
             return;
         }
@@ -265,7 +267,8 @@
                     + ", Client: " + mStatsClient
                     + ", Latency: " + latency
                     + ", Lux: " + mALSProbe.getMostRecentLux()
-                    + ", Success: " + enrollSuccessful);
+                    + ", Success: " + enrollSuccessful
+                    + ", TemplateId: " + templateId);
         } else {
             Slog.v(TAG, "Enroll latency: " + latency);
         }
@@ -275,7 +278,51 @@
         }
 
         mSink.enroll(mStatsModality, mStatsAction, mStatsClient,
-                targetUserId, latency, enrollSuccessful, mALSProbe.getMostRecentLux(), source);
+                targetUserId, latency, enrollSuccessful, mALSProbe.getMostRecentLux(), source,
+                templateId);
+    }
+
+    /** Log un-enrollment. */
+    public void logOnUnEnrolled(int targetUserId, int reason, int templateId) {
+        if (!mShouldLogMetrics) {
+            return;
+        }
+
+        if (DEBUG) {
+            Slog.v(TAG, "UnEnrolled! Modality: " + mStatsModality
+                    + ", User: " + targetUserId
+                    + ", reason: " + reason
+                    + ", templateId: " + templateId);
+        }
+
+        if (shouldSkipLogging()) {
+            return;
+        }
+
+        mSink.unenrolled(mStatsModality, targetUserId, reason, templateId);
+    }
+
+    /** Log enumeration. */
+    public void logOnEnumerated(int targetUserId, int result, int[] templateIdsHal,
+            int[] templateIdsFramework) {
+        if (!mShouldLogMetrics) {
+            return;
+        }
+
+        if (DEBUG) {
+            Slog.v(TAG, "Enumerated! Modality: " + mStatsModality
+                    + ", User: " + targetUserId
+                    + ", result: " + result
+                    + ", templateIdsHal: " + Arrays.toString(templateIdsHal)
+                    + ", templateIdsFramework: " + Arrays.toString(templateIdsFramework));
+        }
+
+        if (shouldSkipLogging()) {
+            return;
+        }
+
+        mSink.enumerated(mStatsModality, targetUserId, result, templateIdsHal,
+                templateIdsFramework);
     }
 
     /** Report unexpected enrollment reported by the HAL. */
diff --git a/services/core/java/com/android/server/biometrics/sensors/BiometricSchedulerOperation.java b/services/core/java/com/android/server/biometrics/sensors/BiometricSchedulerOperation.java
index a118415..21c5140 100644
--- a/services/core/java/com/android/server/biometrics/sensors/BiometricSchedulerOperation.java
+++ b/services/core/java/com/android/server/biometrics/sensors/BiometricSchedulerOperation.java
@@ -162,7 +162,7 @@
                 STATE_WAITING_IN_QUEUE,
                 STATE_WAITING_FOR_COOKIE,
                 STATE_WAITING_IN_QUEUE_CANCELING)) {
-            return false;
+            return hasOperationAlreadyStarted();
         }
 
         if (mClientMonitor.getCookie() != 0) {
@@ -191,7 +191,7 @@
                 STATE_WAITING_IN_QUEUE,
                 STATE_WAITING_FOR_COOKIE,
                 STATE_WAITING_IN_QUEUE_CANCELING)) {
-            return false;
+            return hasOperationAlreadyStarted();
         }
 
         return doStart(callback);
@@ -230,6 +230,10 @@
         return true;
     }
 
+    private boolean hasOperationAlreadyStarted() {
+        return mState == STATE_STARTED;
+    }
+
     /**
      * Abort a pending operation.
      *
diff --git a/services/core/java/com/android/server/biometrics/sensors/EnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/EnrollClient.java
index 32c0bd3..38bf999 100644
--- a/services/core/java/com/android/server/biometrics/sensors/EnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/EnrollClient.java
@@ -95,7 +95,7 @@
             mBiometricUtils.addBiometricForUser(getContext(), getTargetUserId(), identifier);
             getLogger().logOnEnrolled(getTargetUserId(),
                     System.currentTimeMillis() - mEnrollmentStartTimeMs,
-                    true /* enrollSuccessful */, mEnrollReason);
+                    true /* enrollSuccessful */, mEnrollReason, identifier.getBiometricId());
             mCallback.onClientFinished(this, true /* success */);
         }
         notifyUserActivity();
@@ -123,7 +123,7 @@
     public void onError(int error, int vendorCode) {
         getLogger().logOnEnrolled(getTargetUserId(),
                 System.currentTimeMillis() - mEnrollmentStartTimeMs,
-                false /* enrollSuccessful */, mEnrollReason);
+                false /* enrollSuccessful */, mEnrollReason, -1 /* templateId */);
         super.onError(error, vendorCode);
     }
 
diff --git a/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java b/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java
index 6c30559..03b382e 100644
--- a/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java
@@ -19,6 +19,7 @@
 import android.annotation.NonNull;
 import android.content.Context;
 import android.hardware.biometrics.BiometricAuthenticator;
+import android.hardware.biometrics.BiometricsProtoEnums;
 import android.os.Build;
 import android.os.IBinder;
 import android.util.Slog;
@@ -135,7 +136,7 @@
             Supplier<T> lazyDaemon, IBinder token, int biometricId, int userId, String owner,
             BiometricUtils<S> utils, int sensorId,
             @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
-            Map<Integer, Long> authenticatorIds);
+            Map<Integer, Long> authenticatorIds, int reason);
 
     protected InternalCleanupClient(@NonNull Context context, @NonNull Supplier<T> lazyDaemon,
             int userId, @NonNull String owner, int sensorId,
@@ -158,7 +159,8 @@
         mCurrentTask = getRemovalClient(getContext(), mLazyDaemon, getToken(),
                 template.mIdentifier.getBiometricId(), template.mUserId,
                 getContext().getPackageName(), mBiometricUtils, getSensorId(),
-                getLogger(), getBiometricContext(), mAuthenticatorIds);
+                getLogger(), getBiometricContext(), mAuthenticatorIds,
+                BiometricsProtoEnums.UNENROLL_REASON_DANGLING_HAL);
 
         getLogger().logUnknownEnrollmentInHal();
 
diff --git a/services/core/java/com/android/server/biometrics/sensors/InternalEnumerateClient.java b/services/core/java/com/android/server/biometrics/sensors/InternalEnumerateClient.java
index 2c2c685..5a47f1be 100644
--- a/services/core/java/com/android/server/biometrics/sensors/InternalEnumerateClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/InternalEnumerateClient.java
@@ -19,6 +19,7 @@
 import android.annotation.NonNull;
 import android.content.Context;
 import android.hardware.biometrics.BiometricAuthenticator;
+import android.hardware.biometrics.BiometricsProtoEnums;
 import android.os.IBinder;
 import android.util.Slog;
 
@@ -46,6 +47,10 @@
     // List of templates to remove from the HAL
     private List<BiometricAuthenticator.Identifier> mUnknownHALTemplates = new ArrayList<>();
     private final int mInitialEnrolledSize;
+    private final int[] mEnrolledIdsFrameworkArray;
+    private final List<Integer> mEnrolledIdsHalList = new ArrayList<>();
+    private boolean mIsDanglingFramework;
+    private boolean mIsDanglingHal;
 
     protected InternalEnumerateClient(@NonNull Context context, @NonNull Supplier<T> lazyDaemon,
             @NonNull IBinder token, int userId, @NonNull String owner,
@@ -60,14 +65,26 @@
         mEnrolledList = enrolledList;
         mInitialEnrolledSize = mEnrolledList.size();
         mUtils = utils;
+        // Record ids from frameworks for metrics
+        mEnrolledIdsFrameworkArray = new int[mInitialEnrolledSize];
+        for (int i = 0; i < mInitialEnrolledSize; i++) {
+            mEnrolledIdsFrameworkArray[i] = mEnrolledList.get(i).getBiometricId();
+        }
     }
 
     @Override
     public void onEnumerationResult(BiometricAuthenticator.Identifier identifier,
             int remaining) {
+        if (identifier != null) {
+            // Record ids from hal for metrics
+            mEnrolledIdsHalList.add(identifier.getBiometricId());
+        }
         handleEnumeratedTemplate(identifier);
         if (remaining == 0) {
+            mIsDanglingHal = !mUnknownHALTemplates.isEmpty();
+            mIsDanglingFramework = !mEnrolledList.isEmpty();
             doTemplateCleanup();
+            logEnumerationResult();
             mCallback.onClientFinished(this, true /* success */);
         }
     }
@@ -123,7 +140,9 @@
                     + identifier.getBiometricId() + " " + identifier.getName());
             mUtils.removeBiometricForUser(getContext(),
                     getTargetUserId(), identifier.getBiometricId());
-
+            getLogger().logOnUnEnrolled(getTargetUserId(),
+                    BiometricsProtoEnums.UNENROLL_REASON_DANGLING_FRAMEWORK,
+                    identifier.getBiometricId());
             getLogger().logUnknownEnrollmentInFramework();
         }
 
@@ -134,6 +153,32 @@
         mEnrolledList.clear();
     }
 
+    private void logEnumerationResult() {
+        final int result;
+        if (mIsDanglingFramework && mIsDanglingHal) {
+            result = BiometricsProtoEnums.ENUMERATION_RESULT_DANGLING_BOTH;
+        } else if (mIsDanglingFramework) {
+            result = BiometricsProtoEnums.ENUMERATION_RESULT_DANGLING_FRAMEWORK;
+        } else if (mIsDanglingHal) {
+            result = BiometricsProtoEnums.ENUMERATION_RESULT_DANGLING_HAL;
+        } else {
+            result = BiometricsProtoEnums.ENUMERATION_RESULT_OK;
+        }
+
+        final int[] idsHalArray = listToArray(mEnrolledIdsHalList);
+        getLogger().logOnEnumerated(
+                getTargetUserId(), result, idsHalArray, mEnrolledIdsFrameworkArray);
+    }
+
+    private int[] listToArray(List<Integer> ids) {
+        final int size = ids.size();
+        int[] array = new int[size];
+        for (int i = 0; i < size; i++) {
+            array[i] = ids.get(i);
+        }
+        return array;
+    }
+
     public List<BiometricAuthenticator.Identifier> getUnknownHALTemplates() {
         return mUnknownHALTemplates;
     }
diff --git a/services/core/java/com/android/server/biometrics/sensors/RemovalClient.java b/services/core/java/com/android/server/biometrics/sensors/RemovalClient.java
index ad5877a..0259906 100644
--- a/services/core/java/com/android/server/biometrics/sensors/RemovalClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/RemovalClient.java
@@ -42,17 +42,19 @@
     private final BiometricUtils<S> mBiometricUtils;
     private final Map<Integer, Long> mAuthenticatorIds;
     private final boolean mHasEnrollmentsBeforeStarting;
+    private final int mReason;
 
     public RemovalClient(@NonNull Context context, @NonNull Supplier<T> lazyDaemon,
             @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener,
             int userId, @NonNull String owner, @NonNull BiometricUtils<S> utils, int sensorId,
             @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
-            @NonNull Map<Integer, Long> authenticatorIds) {
+            @NonNull Map<Integer, Long> authenticatorIds, int reason) {
         super(context, lazyDaemon, token, listener, userId, owner, 0 /* cookie */, sensorId,
                 logger, biometricContext, false /* isMandatoryBiometrics */);
         mBiometricUtils = utils;
         mAuthenticatorIds = authenticatorIds;
         mHasEnrollmentsBeforeStarting = !utils.getBiometricsForUser(context, userId).isEmpty();
+        mReason = reason;
     }
 
     @Override
@@ -87,6 +89,7 @@
         Slog.d(TAG, "onRemoved: " + identifier.getBiometricId() + " remaining: " + remaining);
         mBiometricUtils.removeBiometricForUser(getContext(), getTargetUserId(),
                 identifier.getBiometricId());
+        getLogger().logOnUnEnrolled(getTargetUserId(), mReason, identifier.getBiometricId());
 
         try {
             getListener().onRemoved(identifier, remaining);
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceInternalCleanupClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceInternalCleanupClient.java
index c27b7c4..5adc251 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceInternalCleanupClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceInternalCleanupClient.java
@@ -63,12 +63,12 @@
             Supplier<AidlSession> lazyDaemon, IBinder token,
             int biometricId, int userId, String owner, BiometricUtils<Face> utils, int sensorId,
             @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
-            Map<Integer, Long> authenticatorIds) {
+            Map<Integer, Long> authenticatorIds, int reason) {
         // Internal remove does not need to send results to anyone. Cleanup (enumerate + remove)
         // is all done internally.
         return new FaceRemovalClient(context, lazyDaemon, token,
                 null /* ClientMonitorCallbackConverter */, new int[] {biometricId}, userId, owner,
-                utils, sensorId, logger, biometricContext, authenticatorIds);
+                utils, sensorId, logger, biometricContext, authenticatorIds, reason);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
index 5127e68..36e4a7e 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
@@ -668,7 +668,8 @@
                             BiometricsProtoEnums.CLIENT_UNKNOWN,
                             mAuthenticationStatsCollector),
                     mBiometricContext,
-                    mFaceSensors.get(sensorId).getAuthenticatorIds());
+                    mFaceSensors.get(sensorId).getAuthenticatorIds(),
+                    BiometricsProtoEnums.UNENROLL_REASON_USER_REQUEST);
             scheduleForSensor(sensorId, client, mBiometricStateCallback);
         });
     }
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceRemovalClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceRemovalClient.java
index 0793888..971cfbb 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceRemovalClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceRemovalClient.java
@@ -46,9 +46,9 @@
             int[] biometricIds, int userId, @NonNull String owner,
             @NonNull BiometricUtils<Face> utils, int sensorId,
             @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
-            @NonNull Map<Integer, Long> authenticatorIds) {
+            @NonNull Map<Integer, Long> authenticatorIds, int reason) {
         super(context, lazyDaemon, token, listener, userId, owner, utils, sensorId,
-                logger, biometricContext, authenticatorIds);
+                logger, biometricContext, authenticatorIds, reason);
         mBiometricIds = biometricIds;
     }
 
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalCleanupClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalCleanupClient.java
index 40b8a45..77670ec 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalCleanupClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalCleanupClient.java
@@ -71,11 +71,11 @@
             Supplier<AidlSession> lazyDaemon, IBinder token, int biometricId, int userId,
             String owner, BiometricUtils<Fingerprint> utils, int sensorId,
             @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
-            Map<Integer, Long> authenticatorIds) {
+            Map<Integer, Long> authenticatorIds, int reason) {
         return new FingerprintRemovalClient(context, lazyDaemon, token,
                 null /* ClientMonitorCallbackConverter */, new int[] {biometricId}, userId, owner,
                 utils, sensorId, logger.swapAction(context, BiometricsProtoEnums.ACTION_REMOVE),
-                biometricContext, authenticatorIds);
+                biometricContext, authenticatorIds, reason);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
index 456591c..c18d925 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
@@ -659,7 +659,8 @@
                     sensorId, createLogger(BiometricsProtoEnums.ACTION_REMOVE,
                     BiometricsProtoEnums.CLIENT_UNKNOWN,
                     mAuthenticationStatsCollector), mBiometricContext,
-                    mFingerprintSensors.get(sensorId).getAuthenticatorIds());
+                    mFingerprintSensors.get(sensorId).getAuthenticatorIds(),
+                    BiometricsProtoEnums.UNENROLL_REASON_USER_REQUEST);
             scheduleForSensor(sensorId, client, mBiometricStateCallback);
         });
     }
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintRemovalClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintRemovalClient.java
index 4f08f6f..c6b955a 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintRemovalClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintRemovalClient.java
@@ -47,9 +47,9 @@
             @Nullable ClientMonitorCallbackConverter listener, int[] biometricIds, int userId,
             @NonNull String owner, @NonNull BiometricUtils<Fingerprint> utils, int sensorId,
             @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
-            @NonNull Map<Integer, Long> authenticatorIds) {
+            @NonNull Map<Integer, Long> authenticatorIds, int reason) {
         super(context, lazyDaemon, token, listener, userId, owner, utils, sensorId,
-                logger, biometricContext, authenticatorIds);
+                logger, biometricContext, authenticatorIds, reason);
         mBiometricIds = biometricIds;
     }
 
diff --git a/services/core/java/com/android/server/display/DisplayTopology.java b/services/core/java/com/android/server/display/DisplayTopology.java
index b01d617..fdadafe 100644
--- a/services/core/java/com/android/server/display/DisplayTopology.java
+++ b/services/core/java/com/android/server/display/DisplayTopology.java
@@ -16,7 +16,13 @@
 
 package com.android.server.display;
 
+import static com.android.server.display.DisplayTopology.TreeNode.Position.POSITION_BOTTOM;
+import static com.android.server.display.DisplayTopology.TreeNode.Position.POSITION_LEFT;
+import static com.android.server.display.DisplayTopology.TreeNode.Position.POSITION_TOP;
+import static com.android.server.display.DisplayTopology.TreeNode.Position.POSITION_RIGHT;
+
 import android.annotation.Nullable;
+import android.graphics.RectF;
 import android.util.IndentingPrintWriter;
 import android.util.Pair;
 import android.util.Slog;
@@ -25,16 +31,21 @@
 import com.android.internal.annotations.VisibleForTesting;
 
 import java.io.PrintWriter;
+import java.util.ArrayDeque;
 import java.util.ArrayList;
-import java.util.LinkedList;
+import java.util.Comparator;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.Queue;
 
 /**
  * Represents the relative placement of extended displays.
+ * Does not support concurrent calls, so a lock should be held when calling into this class.
  */
 class DisplayTopology {
     private static final String TAG = "DisplayTopology";
+    private static final float EPSILON = 0.0001f;
 
     /**
      * The topology tree
@@ -58,7 +69,7 @@
      * @param width The width of the display
      * @param height The height of the display
      */
-    void addDisplay(int displayId, double width, double height) {
+    void addDisplay(int displayId, float width, float height) {
         addDisplay(displayId, width, height, /* shouldLog= */ true);
     }
 
@@ -69,10 +80,10 @@
      * @param displayId The logical display ID
      */
     void removeDisplay(int displayId) {
-        if (!isDisplayPresent(displayId, mRoot)) {
+        if (findDisplay(displayId, mRoot) == null) {
             return;
         }
-        Queue<TreeNode> queue = new LinkedList<>();
+        Queue<TreeNode> queue = new ArrayDeque<>();
         queue.add(mRoot);
         mRoot = null;
         while (!queue.isEmpty()) {
@@ -115,7 +126,11 @@
         }
     }
 
-    private void addDisplay(int displayId, double width, double height, boolean shouldLog) {
+    private void addDisplay(int displayId, float width, float height, boolean shouldLog) {
+        if (findDisplay(displayId, mRoot) != null) {
+            throw new IllegalArgumentException(
+                    "DisplayTopology: attempting to add a display that already exists");
+        }
         if (mRoot == null) {
             mRoot = new TreeNode(displayId, width, height, /* position= */ null, /* offset= */ 0);
             mPrimaryDisplayId = displayId;
@@ -124,9 +139,8 @@
             }
         } else if (mRoot.mChildren.isEmpty()) {
             // This is the 2nd display. Align the middles of the top and bottom edges.
-            double offset = mRoot.mWidth / 2 - width / 2;
-            TreeNode display = new TreeNode(displayId, width, height,
-                    TreeNode.Position.POSITION_TOP, offset);
+            float offset = mRoot.mWidth / 2 - width / 2;
+            TreeNode display = new TreeNode(displayId, width, height, POSITION_TOP, offset);
             mRoot.mChildren.add(display);
             if (shouldLog) {
                 Slog.i(TAG, "Second display added: " + display + ", parent ID: "
@@ -134,8 +148,8 @@
             }
         } else {
             TreeNode rightMostDisplay = findRightMostDisplay(mRoot, mRoot.mWidth).first;
-            TreeNode newDisplay = new TreeNode(displayId, width, height,
-                    TreeNode.Position.POSITION_RIGHT, /* offset= */ 0);
+            TreeNode newDisplay = new TreeNode(displayId, width, height, POSITION_RIGHT,
+                    /* offset= */ 0);
             rightMostDisplay.mChildren.add(newDisplay);
             if (shouldLog) {
                 Slog.i(TAG, "Display added: " + newDisplay + ", parent ID: "
@@ -150,11 +164,11 @@
      * @return The display that is the furthest to the right and the x position of the right edge
      * of that display
      */
-    private Pair<TreeNode, Double> findRightMostDisplay(TreeNode display, double xPos) {
-        Pair<TreeNode, Double> result = new Pair<>(display, xPos);
+    private static Pair<TreeNode, Float> findRightMostDisplay(TreeNode display, float xPos) {
+        Pair<TreeNode, Float> result = new Pair<>(display, xPos);
         for (TreeNode child : display.mChildren) {
             // The x position of the right edge of the child
-            double childXPos;
+            float childXPos;
             switch (child.mPosition) {
                 case POSITION_LEFT -> childXPos = xPos - display.mWidth;
                 case POSITION_TOP, POSITION_BOTTOM ->
@@ -164,7 +178,7 @@
             }
 
             // Recursive call - find the rightmost display starting from the child
-            Pair<TreeNode, Double> childResult = findRightMostDisplay(child, childXPos);
+            Pair<TreeNode, Float> childResult = findRightMostDisplay(child, childXPos);
             // Check if the one found is further right
             if (childResult.second > result.second) {
                 result = new Pair<>(childResult.first, childResult.second);
@@ -173,19 +187,200 @@
         return result;
     }
 
-    private boolean isDisplayPresent(int displayId, TreeNode node) {
-        if (node == null) {
-            return false;
+    @Nullable
+    private static TreeNode findDisplay(int displayId, TreeNode startingNode) {
+        if (startingNode == null) {
+            return null;
         }
-        if (node.mDisplayId == displayId) {
-            return true;
+        if (startingNode.mDisplayId == displayId) {
+            return startingNode;
         }
-        for (TreeNode child : node.mChildren) {
-            if (isDisplayPresent(displayId, child)) {
-                return true;
+        for (TreeNode child : startingNode.mChildren) {
+            TreeNode display = findDisplay(displayId, child);
+            if (display != null) {
+                return display;
             }
         }
-        return false;
+        return null;
+    }
+
+    /**
+     * Get information about the topology that will be used for the normalization algorithm.
+     * Assigns origins to each display to compute the bounds.
+     * @param bounds The map where the bounds of each display will be put
+     * @param depths The map where the depths of each display in the tree will be put
+     * @param parents The map where the parent of each display will be put
+     * @param display The starting node
+     * @param x The starting x position
+     * @param y The starting y position
+     * @param depth The starting depth
+     */
+    private static void getInfo(Map<TreeNode, RectF> bounds, Map<TreeNode, Integer> depths,
+            Map<TreeNode, TreeNode> parents, TreeNode display, float x, float y, int depth) {
+        bounds.put(display, new RectF(x, y, x + display.mWidth, y + display.mHeight));
+        depths.put(display, depth);
+        for (TreeNode child : display.mChildren) {
+            parents.put(child, display);
+            if (child.mPosition == POSITION_LEFT) {
+                getInfo(bounds, depths, parents, child, x - child.mWidth, y + child.mOffset,
+                        depth + 1);
+            } else if (child.mPosition == POSITION_RIGHT) {
+                getInfo(bounds, depths, parents, child, x + display.mWidth, y + child.mOffset,
+                        depth + 1);
+            } else if (child.mPosition == POSITION_TOP) {
+                getInfo(bounds, depths, parents, child, x + child.mOffset, y - child.mHeight,
+                        depth + 1);
+            } else if (child.mPosition == POSITION_BOTTOM) {
+                getInfo(bounds, depths, parents, child, x + child.mOffset, y + display.mHeight,
+                        depth + 1);
+            }
+        }
+    }
+
+    /**
+     * Update the topology to remove any overlaps between displays.
+     */
+    @VisibleForTesting
+    void normalize() {
+        if (mRoot == null) {
+            return;
+        }
+        Map<TreeNode, RectF> bounds = new HashMap<>();
+        Map<TreeNode, Integer> depths = new HashMap<>();
+        Map<TreeNode, TreeNode> parents = new HashMap<>();
+        getInfo(bounds, depths, parents, mRoot, /* x= */ 0, /* y= */ 0, /* depth= */ 0);
+
+        // Sort the displays first by their depth in the tree, then by the distance of their top
+        // left point from the root display's origin (0, 0). This way we process the displays
+        // starting at the root and we push out a display if necessary.
+        Comparator<TreeNode> comparator = (d1, d2) -> {
+            if (d1 == d2) {
+                return 0;
+            }
+
+            int compareDepths = Integer.compare(depths.get(d1), depths.get(d2));
+            if (compareDepths != 0) {
+                return compareDepths;
+            }
+
+            RectF bounds1 = bounds.get(d1);
+            RectF bounds2 = bounds.get(d2);
+            return Double.compare(Math.hypot(bounds1.left, bounds1.top),
+                    Math.hypot(bounds2.left, bounds2.top));
+        };
+        List<TreeNode> displays = new ArrayList<>(bounds.keySet());
+        displays.sort(comparator);
+
+        for (int i = 1; i < displays.size(); i++) {
+            TreeNode targetDisplay = displays.get(i);
+            TreeNode lastIntersectingSourceDisplay = null;
+            float lastOffsetX = 0;
+            float lastOffsetY = 0;
+
+            for (int j = 0; j < i; j++) {
+                TreeNode sourceDisplay = displays.get(j);
+                RectF sourceBounds = bounds.get(sourceDisplay);
+                RectF targetBounds = bounds.get(targetDisplay);
+
+                if (!RectF.intersects(sourceBounds, targetBounds)) {
+                    continue;
+                }
+
+                // Find the offset by which to move the display. Pick the smaller one among the x
+                // and y axes.
+                float offsetX = targetBounds.left >= 0
+                        ? sourceBounds.right - targetBounds.left
+                        : sourceBounds.left - targetBounds.right;
+                float offsetY = targetBounds.top >= 0
+                        ? sourceBounds.bottom - targetBounds.top
+                        : sourceBounds.top - targetBounds.bottom;
+                if (Math.abs(offsetX) <= Math.abs(offsetY)) {
+                    targetBounds.left += offsetX;
+                    targetBounds.right += offsetX;
+                    // We need to also update the offset in the tree
+                    if (targetDisplay.mPosition == POSITION_TOP
+                            || targetDisplay.mPosition == POSITION_BOTTOM) {
+                        targetDisplay.mOffset += offsetX;
+                    }
+                    offsetY = 0;
+                } else {
+                    targetBounds.top += offsetY;
+                    targetBounds.bottom += offsetY;
+                    // We need to also update the offset in the tree
+                    if (targetDisplay.mPosition == POSITION_LEFT
+                            || targetDisplay.mPosition == POSITION_RIGHT) {
+                        targetDisplay.mOffset += offsetY;
+                    }
+                    offsetX = 0;
+                }
+
+                lastIntersectingSourceDisplay = sourceDisplay;
+                lastOffsetX = offsetX;
+                lastOffsetY = offsetY;
+            }
+
+            // Now re-parent the target display to the last intersecting source display if it no
+            // longer touches its parent.
+            if (lastIntersectingSourceDisplay == null) {
+                // There was no overlap.
+                continue;
+            }
+            TreeNode parent = parents.get(targetDisplay);
+            if (parent == lastIntersectingSourceDisplay) {
+                // The displays are moved in such a way that they're adjacent to the intersecting
+                // display. If the last intersecting display happens to be the parent then we
+                // already know that the display is adjacent to its parent.
+                continue;
+            }
+
+            RectF childBounds = bounds.get(targetDisplay);
+            RectF parentBounds = bounds.get(parent);
+            // Check that the edges are on the same line
+            boolean areTouching = switch (targetDisplay.mPosition) {
+                case POSITION_LEFT -> floatEquals(parentBounds.left, childBounds.right);
+                case POSITION_RIGHT -> floatEquals(parentBounds.right, childBounds.left);
+                case POSITION_TOP -> floatEquals(parentBounds.top, childBounds.bottom);
+                case POSITION_BOTTOM -> floatEquals(parentBounds.bottom, childBounds.top);
+            };
+            // Check that the offset is within bounds
+            areTouching &= switch (targetDisplay.mPosition) {
+                case POSITION_LEFT, POSITION_RIGHT ->
+                        childBounds.bottom + EPSILON >= parentBounds.top
+                                && childBounds.top <= parentBounds.bottom + EPSILON;
+                case POSITION_TOP, POSITION_BOTTOM ->
+                        childBounds.right + EPSILON >= parentBounds.left
+                                && childBounds.left <= parentBounds.right + EPSILON;
+            };
+
+            if (!areTouching) {
+                // Re-parent the display.
+                parent.mChildren.remove(targetDisplay);
+                RectF lastIntersectingSourceDisplayBounds =
+                        bounds.get(lastIntersectingSourceDisplay);
+                lastIntersectingSourceDisplay.mChildren.add(targetDisplay);
+
+                if (lastOffsetX != 0) {
+                    targetDisplay.mPosition = lastOffsetX > 0 ? POSITION_RIGHT : POSITION_LEFT;
+                    targetDisplay.mOffset =
+                            childBounds.top - lastIntersectingSourceDisplayBounds.top;
+                } else if (lastOffsetY != 0) {
+                    targetDisplay.mPosition = lastOffsetY > 0 ? POSITION_BOTTOM : POSITION_TOP;
+                    targetDisplay.mOffset =
+                            childBounds.left - lastIntersectingSourceDisplayBounds.left;
+                }
+            }
+        }
+    }
+
+    /**
+     * Tests whether two brightness float values are within a small enough tolerance
+     * of each other.
+     * @param a first float to compare
+     * @param b second float to compare
+     * @return whether the two values are within a small enough tolerance value
+     */
+    public static boolean floatEquals(float a, float b) {
+        return a == b || Float.isNaN(a) && Float.isNaN(b) || Math.abs(a - b) < EPSILON;
     }
 
     @VisibleForTesting
@@ -201,13 +396,13 @@
          * The width of the display in density-independent pixels (dp).
          */
         @VisibleForTesting
-        double mWidth;
+        float mWidth;
 
         /**
          * The height of the display in density-independent pixels (dp).
          */
         @VisibleForTesting
-        double mHeight;
+        float mHeight;
 
         /**
          * The position of this display relative to its parent.
@@ -222,13 +417,12 @@
          * used is density-independent pixels (dp).
          */
         @VisibleForTesting
-        double mOffset;
+        float mOffset;
 
         @VisibleForTesting
         final List<TreeNode> mChildren = new ArrayList<>();
 
-        TreeNode(int displayId, double width, double height, Position position,
-                double offset) {
+        TreeNode(int displayId, float width, float height, Position position, float offset) {
             mDisplayId = displayId;
             mWidth = width;
             mHeight = height;
diff --git a/services/core/java/com/android/server/display/DisplayTopologyCoordinator.java b/services/core/java/com/android/server/display/DisplayTopologyCoordinator.java
index 46358dfd..b101e58 100644
--- a/services/core/java/com/android/server/display/DisplayTopologyCoordinator.java
+++ b/services/core/java/com/android/server/display/DisplayTopologyCoordinator.java
@@ -89,8 +89,8 @@
      * @param info The display info
      * @return The width of the display in dp
      */
-    private double getWidth(DisplayInfo info) {
-        return info.logicalWidth * (double) DisplayMetrics.DENSITY_DEFAULT
+    private float getWidth(DisplayInfo info) {
+        return info.logicalWidth * (float) DisplayMetrics.DENSITY_DEFAULT
                 / info.logicalDensityDpi;
     }
 
@@ -98,8 +98,8 @@
      * @param info The display info
      * @return The height of the display in dp
      */
-    private double getHeight(DisplayInfo info) {
-        return info.logicalHeight * (double) DisplayMetrics.DENSITY_DEFAULT
+    private float getHeight(DisplayInfo info) {
+        return info.logicalHeight * (float) DisplayMetrics.DENSITY_DEFAULT
                 / info.logicalDensityDpi;
     }
 
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
index d0ad6fc..b696c54 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
@@ -444,14 +444,62 @@
                         new Runnable() {
                             @Override
                             public void run() {
-                                if (!isActiveSource()) {
+                                if (isActiveSource()) {
+                                    return;
+                                }
+
+                                if (getActiveSource().logicalAddress != Constants.ADDR_TV) {
                                     startHdmiCecActiveSourceLostActivity();
                                     mDelayedStandbyOnActiveSourceLostHandler
                                             .removeCallbacksAndMessages(null);
                                     mDelayedStandbyOnActiveSourceLostHandler.postDelayed(
                                             new DelayedStandbyOnActiveSourceLostRunnable(),
                                             STANDBY_AFTER_ACTIVE_SOURCE_LOST_DELAY_MS);
+                                    return;
                                 }
+
+                                // We observed specific TV panels (old models) that send faulty CEC
+                                // source changing messages, especially during wake-up.
+                                // This request helps to check if the TV correctly asserted active
+                                // source or not. If the request times out, active source is
+                                // asserted by the local device.
+                                addAndStartAction(new RequestActiveSourceAction(mService.playback(),
+                                        new IHdmiControlCallback.Stub() {
+                                    @Override
+                                    public void onComplete(int result) {
+                                        // If a device answers to <Request Active Source>, the
+                                        // pop-up should be triggered.
+                                        // During this action, the TV can switch to an HDMI input
+                                        // with a non-CEC capable device that won't be able to
+                                        // answer this request.
+                                        // In this case, the known active source would be
+                                        // represented by a valid physical address, but invalid
+                                        // logical address. The pop-up will be shown and the local
+                                        // device will not assert active source.
+                                        if (result == HdmiControlManager.RESULT_SUCCESS
+                                                || getActiveSource().logicalAddress
+                                                != Constants.ADDR_TV) {
+                                                startHdmiCecActiveSourceLostActivity();
+                                                mDelayedStandbyOnActiveSourceLostHandler
+                                                        .removeCallbacksAndMessages(null);
+                                                mDelayedStandbyOnActiveSourceLostHandler
+                                                        .postDelayed(
+                                                                new DelayedStandbyOnActiveSourceLostRunnable(),
+                                                        STANDBY_AFTER_ACTIVE_SOURCE_LOST_DELAY_MS);
+                                        } else {
+                                            // The request times out and the local device is not
+                                            // active source, but the TV previously asserted active
+                                            // source.
+                                            if (getActiveSource().logicalAddress
+                                                    == Constants.ADDR_TV) {
+                                                mService.setAndBroadcastActiveSource(
+                                                        mService.getPhysicalAddress(),
+                                                        getDeviceInfo().getDeviceType(),
+                                                        Constants.ADDR_BROADCAST,
+                                                        "RequestActiveSourceAction#RESULT_TIMEOUT");
+                                            }
+                                        }
+                                    }}));
                             }
                         }, POPUP_AFTER_ACTIVE_SOURCE_LOST_DELAY_MS);
                 return;
@@ -698,6 +746,7 @@
         removeAction(HotplugDetectionAction.class);
         removeAction(NewDeviceAction.class);
         removeAction(PowerStatusMonitorActionFromPlayback.class);
+        removeAction(RequestActiveSourceAction.class);
         super.disableDevice(initiatedByCec, callback);
         clearDeviceInfoList();
         checkIfPendingActionsCleared();
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index aae7b59..bf415a3 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -471,6 +471,10 @@
     void startRoutingControl(int oldPath, int newPath, IHdmiControlCallback callback) {
         assertRunOnServiceThread();
         if (oldPath == newPath) {
+            HdmiCecMessage setStreamPath =
+                    HdmiCecMessageBuilder.buildSetStreamPath(getDeviceInfo().getLogicalAddress(),
+                            oldPath);
+            mService.sendCecCommand(setStreamPath);
             return;
         }
         HdmiCecMessage routingChange =
@@ -642,7 +646,8 @@
         int address = message.getSource();
         int type = message.getParams()[2];
 
-        if (!mService.getHdmiCecNetwork().isInDeviceList(address, path)) {
+        if (!ActiveSource.of(address, path).equals(getActiveSource())) {
+            HdmiLogger.debug("Check if a new device is connected to the active path");
             handleNewDeviceAtTheTailOfActivePath(path);
         }
         startNewDeviceAction(ActiveSource.of(address, path), type);
@@ -792,6 +797,10 @@
                     @Override
                     public void onDeviceDiscoveryDone(List<HdmiDeviceInfo> deviceInfos) {
                         for (HdmiDeviceInfo info : deviceInfos) {
+                            if (!isInputReady(info.getDeviceId())) {
+                                mService.getHdmiCecNetwork().removeCecDevice(
+                                        HdmiCecLocalDeviceTv.this, info.getLogicalAddress());
+                            }
                             mService.getHdmiCecNetwork().addCecDevice(info);
                         }
 
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index 81be0ba..0766c3a 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -1988,7 +1988,6 @@
     void setAudioStatus(boolean mute, int volume) {
         if (!isTvDeviceEnabled()
                 || !tv().isSystemAudioActivated()
-                || !tv().isArcEstablished() // Don't update TV volume when SAM is on and ARC is off
                 || getHdmiCecVolumeControl()
                 == HdmiControlManager.VOLUME_CONTROL_DISABLED) {
             return;
@@ -4344,6 +4343,7 @@
         if (deviceType == HdmiDeviceInfo.DEVICE_PLAYBACK) {
             HdmiCecLocalDevicePlayback playback = playback();
             playback.dismissUiOnActiveSourceStatusRecovered();
+            playback.removeAction(RequestActiveSourceAction.class);
             playback.setActiveSource(playback.getDeviceInfo().getLogicalAddress(), physicalAddress,
                     caller);
             playback.wakeUpIfActiveSource();
diff --git a/services/core/java/com/android/server/hdmi/RequestActiveSourceAction.java b/services/core/java/com/android/server/hdmi/RequestActiveSourceAction.java
index a33d70a..b0e9398 100644
--- a/services/core/java/com/android/server/hdmi/RequestActiveSourceAction.java
+++ b/services/core/java/com/android/server/hdmi/RequestActiveSourceAction.java
@@ -22,11 +22,11 @@
 import com.android.internal.annotations.VisibleForTesting;
 
 /**
- * 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.
+ * Feature action that sends <Request Active Source> message and waits for <Active Source>.
+ *
+ * For 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";
@@ -55,6 +55,13 @@
     boolean start() {
         Slog.v(TAG, "RequestActiveSourceAction started.");
 
+        if (localDevice().mService.isPlaybackDevice()) {
+            mState = STATE_WAIT_FOR_ACTIVE_SOURCE;
+            sendCommand(HdmiCecMessageBuilder.buildRequestActiveSource(getSourceAddress()));
+            addTimer(mState, HdmiConfig.TIMEOUT_MS);
+            return true;
+        }
+
         mState = STATE_WAIT_FOR_LAUNCHERX_API_CALL;
 
         // We wait for default timeout to allow the message triggered by the LauncherX API call to
diff --git a/services/core/java/com/android/server/input/InputGestureManager.java b/services/core/java/com/android/server/input/InputGestureManager.java
new file mode 100644
index 0000000..ae31b33
--- /dev/null
+++ b/services/core/java/com/android/server/input/InputGestureManager.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright 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.input;
+
+import android.annotation.NonNull;
+import android.hardware.input.InputGestureData;
+import android.hardware.input.InputManager;
+import android.util.IndentingPrintWriter;
+import android.util.SparseArray;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+
+/**
+ * A thread-safe component of {@link InputManagerService} responsible for managing pre-defined input
+ * gestures and custom gestures defined by other system components using Input APIs.
+ *
+ * TODO(b/365064144): Add implementation to persist data, identify clashes with existing shortcuts.
+ *
+ */
+final class InputGestureManager {
+    private static final String TAG = "InputGestureManager";
+
+    @GuardedBy("mCustomInputGestures")
+    private final SparseArray<Map<InputGestureData.Trigger, InputGestureData>>
+            mCustomInputGestures = new SparseArray<>();
+
+    @InputManager.CustomInputGestureResult
+    public int addCustomInputGesture(int userId, InputGestureData newGesture) {
+        synchronized (mCustomInputGestures) {
+            if (!mCustomInputGestures.contains(userId)) {
+                mCustomInputGestures.put(userId, new HashMap<>());
+            }
+            Map<InputGestureData.Trigger, InputGestureData> customGestures =
+                    mCustomInputGestures.get(userId);
+            if (customGestures.containsKey(newGesture.getTrigger())) {
+                return InputManager.CUSTOM_INPUT_GESTURE_RESULT_ERROR_ALREADY_EXISTS;
+            }
+            customGestures.put(newGesture.getTrigger(), newGesture);
+            return InputManager.CUSTOM_INPUT_GESTURE_RESULT_SUCCESS;
+        }
+    }
+
+    @InputManager.CustomInputGestureResult
+    public int removeCustomInputGesture(int userId, InputGestureData data) {
+        synchronized (mCustomInputGestures) {
+            if (!mCustomInputGestures.contains(userId)) {
+                return InputManager.CUSTOM_INPUT_GESTURE_RESULT_ERROR_DOES_NOT_EXIST;
+            }
+            Map<InputGestureData.Trigger, InputGestureData> customGestures =
+                    mCustomInputGestures.get(userId);
+            InputGestureData customGesture = customGestures.get(data.getTrigger());
+            if (!Objects.equals(data, customGesture)) {
+                return InputManager.CUSTOM_INPUT_GESTURE_RESULT_ERROR_DOES_NOT_EXIST;
+            }
+            customGestures.remove(data.getTrigger());
+            if (customGestures.size() == 0) {
+                mCustomInputGestures.remove(userId);
+            }
+            return InputManager.CUSTOM_INPUT_GESTURE_RESULT_SUCCESS;
+        }
+    }
+
+    public void removeAllCustomInputGestures(int userId) {
+        synchronized (mCustomInputGestures) {
+            mCustomInputGestures.remove(userId);
+        }
+    }
+
+    @NonNull
+    public List<InputGestureData> getCustomInputGestures(int userId) {
+        synchronized (mCustomInputGestures) {
+            if (!mCustomInputGestures.contains(userId)) {
+                return List.of();
+            }
+            return new ArrayList<>(mCustomInputGestures.get(userId).values());
+        }
+    }
+
+    public void dump(IndentingPrintWriter ipw) {
+        ipw.println("InputGestureManager:");
+        ipw.increaseIndent();
+        synchronized (mCustomInputGestures) {
+            int size = mCustomInputGestures.size();
+            for (int i = 0; i < size; i++) {
+                Map<InputGestureData.Trigger, InputGestureData> customGestures =
+                        mCustomInputGestures.valueAt(i);
+                ipw.println("UserId = " + mCustomInputGestures.keyAt(i));
+                ipw.increaseIndent();
+                for (InputGestureData customGesture : customGestures.values()) {
+                    ipw.println(customGesture);
+                }
+                ipw.decreaseIndent();
+            }
+        }
+        ipw.decreaseIndent();
+    }
+}
diff --git a/services/core/java/com/android/server/input/InputManagerInternal.java b/services/core/java/com/android/server/input/InputManagerInternal.java
index e40d855..1c5bd59 100644
--- a/services/core/java/com/android/server/input/InputManagerInternal.java
+++ b/services/core/java/com/android/server/input/InputManagerInternal.java
@@ -127,6 +127,13 @@
      */
     public abstract void notifyInputMethodConnectionActive(boolean connectionIsActive);
 
+    /**
+     * Notify user id changes to input.
+     *
+     * TODO(b/362473586): Cleanup after input shifts to Lifecycle with user change callbacks
+     */
+    public abstract void setCurrentUser(@UserIdInt int newUserId);
+
     /** Callback interface for notifications relating to the lid switch. */
     public interface LidSwitchCallback {
         /**
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index bea520f..d43ce71 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -51,6 +51,7 @@
 import android.hardware.SensorPrivacyManagerInternal;
 import android.hardware.display.DisplayManagerInternal;
 import android.hardware.display.DisplayViewport;
+import android.hardware.input.AidlInputGestureData;
 import android.hardware.input.HostUsiVersion;
 import android.hardware.input.IInputDeviceBatteryListener;
 import android.hardware.input.IInputDeviceBatteryState;
@@ -63,6 +64,7 @@
 import android.hardware.input.IStickyModifierStateListener;
 import android.hardware.input.ITabletModeChangedListener;
 import android.hardware.input.InputDeviceIdentifier;
+import android.hardware.input.InputGestureData;
 import android.hardware.input.InputManager;
 import android.hardware.input.InputSensorInfo;
 import android.hardware.input.InputSettings;
@@ -175,6 +177,7 @@
     private static final int MSG_DELIVER_INPUT_DEVICES_CHANGED = 1;
     private static final int MSG_RELOAD_DEVICE_ALIASES = 2;
     private static final int MSG_DELIVER_TABLET_MODE_CHANGED = 3;
+    private static final int MSG_CURRENT_USER_CHANGED = 4;
 
     private static final int DEFAULT_VIBRATION_MAGNITUDE = 192;
     private static final AdditionalDisplayInputProperties
@@ -184,6 +187,8 @@
 
     private final Context mContext;
     private final InputManagerHandler mHandler;
+    @UserIdInt
+    private int mCurrentUserId = UserHandle.USER_SYSTEM;
     private DisplayManagerInternal mDisplayManagerInternal;
 
     private WindowManagerInternal mWindowManagerInternal;
@@ -2982,6 +2987,43 @@
         mKeyGestureController.unregisterKeyGestureHandler(handler, Binder.getCallingPid());
     }
 
+    @Override
+    @PermissionManuallyEnforced
+    public int addCustomInputGesture(@NonNull AidlInputGestureData inputGestureData) {
+        enforceManageKeyGesturePermission();
+
+        Objects.requireNonNull(inputGestureData);
+        return mKeyGestureController.addCustomInputGesture(UserHandle.getCallingUserId(),
+                inputGestureData);
+    }
+
+    @Override
+    @PermissionManuallyEnforced
+    public int removeCustomInputGesture(@NonNull AidlInputGestureData inputGestureData) {
+        enforceManageKeyGesturePermission();
+
+        Objects.requireNonNull(inputGestureData);
+        return mKeyGestureController.removeCustomInputGesture(UserHandle.getCallingUserId(),
+                inputGestureData);
+    }
+
+    @Override
+    @PermissionManuallyEnforced
+    public void removeAllCustomInputGestures() {
+        enforceManageKeyGesturePermission();
+
+        mKeyGestureController.removeAllCustomInputGestures(UserHandle.getCallingUserId());
+    }
+
+    @Override
+    public AidlInputGestureData[] getCustomInputGestures() {
+        return mKeyGestureController.getCustomInputGestures(UserHandle.getCallingUserId());
+    }
+
+    private void handleCurrentUserChanged(@UserIdInt int userId) {
+        mCurrentUserId = userId;
+    }
+
     /**
      * Callback interface implemented by the Window Manager.
      */
@@ -3150,6 +3192,9 @@
                     boolean inTabletMode = (boolean) args.arg1;
                     deliverTabletModeChanged(whenNanos, inTabletMode);
                     break;
+                case MSG_CURRENT_USER_CHANGED:
+                    handleCurrentUserChanged((int) msg.obj);
+                    break;
             }
         }
     }
@@ -3513,6 +3558,11 @@
         }
 
         @Override
+        public void setCurrentUser(@UserIdInt int newUserId) {
+            mHandler.obtainMessage(MSG_CURRENT_USER_CHANGED, newUserId).sendToTarget();
+        }
+
+        @Override
         public boolean setKernelWakeEnabled(int deviceId, boolean enabled) {
             return mNative.setKernelWakeEnabled(deviceId, enabled);
         }
diff --git a/services/core/java/com/android/server/input/KeyGestureController.java b/services/core/java/com/android/server/input/KeyGestureController.java
index bd1625e..68eaf72 100644
--- a/services/core/java/com/android/server/input/KeyGestureController.java
+++ b/services/core/java/com/android/server/input/KeyGestureController.java
@@ -28,17 +28,22 @@
 
 import android.annotation.BinderThread;
 import android.annotation.MainThread;
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SuppressLint;
+import android.annotation.UserIdInt;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.content.res.Resources;
 import android.database.ContentObserver;
+import android.hardware.input.AidlInputGestureData;
 import android.hardware.input.AidlKeyGestureEvent;
 import android.hardware.input.IKeyGestureEventListener;
 import android.hardware.input.IKeyGestureHandler;
+import android.hardware.input.InputGestureData;
 import android.hardware.input.InputManager;
+import android.hardware.input.InputSettings;
 import android.hardware.input.KeyGestureEvent;
 import android.os.Handler;
 import android.os.IBinder;
@@ -66,6 +71,7 @@
 
 import java.util.ArrayDeque;
 import java.util.HashSet;
+import java.util.List;
 import java.util.Objects;
 import java.util.Set;
 import java.util.TreeMap;
@@ -108,6 +114,7 @@
     private final int mSystemPid;
     private final KeyCombinationManager mKeyCombinationManager;
     private final SettingsObserver mSettingsObserver;
+    private final InputGestureManager mInputGestureManager = new InputGestureManager();
 
     // Pending actions
     private boolean mPendingMetaAction;
@@ -589,6 +596,54 @@
                     }
                 }
                 break;
+            case KeyEvent.KEYCODE_3:
+                if (InputSettings.isAccessibilityBounceKeysFeatureEnabled()
+                        && keyboardA11yShortcutControl()) {
+                    if (firstDown && event.isMetaPressed() && event.isAltPressed()) {
+                        return handleKeyGesture(deviceId, new int[]{keyCode},
+                                KeyEvent.META_META_ON | KeyEvent.META_ALT_ON,
+                                KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_BOUNCE_KEYS,
+                                KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
+                                focusedToken, /* flags = */0);
+                    }
+                }
+                break;
+            case KeyEvent.KEYCODE_4:
+                if (InputSettings.isAccessibilityMouseKeysFeatureFlagEnabled()
+                        && keyboardA11yShortcutControl()) {
+                    if (firstDown && event.isMetaPressed() && event.isAltPressed()) {
+                        return handleKeyGesture(deviceId, new int[]{keyCode},
+                                KeyEvent.META_META_ON | KeyEvent.META_ALT_ON,
+                                KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_MOUSE_KEYS,
+                                KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
+                                focusedToken, /* flags = */0);
+                    }
+                }
+                break;
+            case KeyEvent.KEYCODE_5:
+                if (InputSettings.isAccessibilityStickyKeysFeatureEnabled()
+                        && keyboardA11yShortcutControl()) {
+                    if (firstDown && event.isMetaPressed() && event.isAltPressed()) {
+                        return handleKeyGesture(deviceId, new int[]{keyCode},
+                                KeyEvent.META_META_ON | KeyEvent.META_ALT_ON,
+                                KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_STICKY_KEYS,
+                                KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
+                                focusedToken, /* flags = */0);
+                    }
+                }
+                break;
+            case KeyEvent.KEYCODE_6:
+                if (InputSettings.isAccessibilitySlowKeysFeatureFlagEnabled()
+                        && keyboardA11yShortcutControl()) {
+                    if (firstDown && event.isMetaPressed() && event.isAltPressed()) {
+                        return handleKeyGesture(deviceId, new int[]{keyCode},
+                                KeyEvent.META_META_ON | KeyEvent.META_ALT_ON,
+                                KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_SLOW_KEYS,
+                                KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
+                                focusedToken, /* flags = */0);
+                    }
+                }
+                break;
             case KeyEvent.KEYCODE_DEL:
                 if (newBugreportKeyboardShortcut()) {
                     if (firstDown && mEnableBugReportKeyboardShortcut && event.isMetaPressed()
@@ -1012,16 +1067,16 @@
         if (device == null) {
             return;
         }
+        KeyGestureEvent keyGestureEvent = new KeyGestureEvent(event);
         if (event.action == KeyGestureEvent.ACTION_GESTURE_COMPLETE) {
             KeyboardMetricsCollector.logKeyboardSystemsEventReportedAtom(device, event.keycodes,
-                    event.modifierState,
-                    KeyGestureEvent.keyGestureTypeToLogEvent(event.gestureType));
+                    event.modifierState, keyGestureEvent.getLogEvent());
         }
         notifyAllListeners(event);
         while (mLastHandledEvents.size() >= MAX_TRACKED_EVENTS) {
             mLastHandledEvents.removeFirst();
         }
-        mLastHandledEvents.addLast(new KeyGestureEvent(event));
+        mLastHandledEvents.addLast(keyGestureEvent);
     }
 
     @MainThread
@@ -1085,6 +1140,37 @@
         }
     }
 
+    @BinderThread
+    @InputManager.CustomInputGestureResult
+    public int addCustomInputGesture(@UserIdInt int userId,
+            @NonNull AidlInputGestureData inputGestureData) {
+        return mInputGestureManager.addCustomInputGesture(userId,
+                new InputGestureData(inputGestureData));
+    }
+
+    @BinderThread
+    @InputManager.CustomInputGestureResult
+    public int removeCustomInputGesture(@UserIdInt int userId,
+            @NonNull AidlInputGestureData inputGestureData) {
+        return mInputGestureManager.removeCustomInputGesture(userId,
+                new InputGestureData(inputGestureData));
+    }
+
+    @BinderThread
+    public void removeAllCustomInputGestures(@UserIdInt int userId) {
+        mInputGestureManager.removeAllCustomInputGestures(userId);
+    }
+
+    @BinderThread
+    public AidlInputGestureData[] getCustomInputGestures(@UserIdInt int userId) {
+        List<InputGestureData> customGestures = mInputGestureManager.getCustomInputGestures(userId);
+        AidlInputGestureData[] result = new AidlInputGestureData[customGestures.size()];
+        for (int i = 0; i < customGestures.size(); i++) {
+            result[i] = customGestures.get(i).getAidlData();
+        }
+        return result;
+    }
+
     private void onKeyGestureEventListenerDied(int pid) {
         synchronized (mKeyGestureEventListenerRecords) {
             mKeyGestureEventListenerRecords.remove(pid);
@@ -1286,5 +1372,6 @@
         }
         ipw.decreaseIndent();
         mKeyCombinationManager.dump("", ipw);
+        mInputGestureManager.dump(ipw);
     }
 }
diff --git a/services/core/java/com/android/server/input/KeyboardGlyphManager.java b/services/core/java/com/android/server/input/KeyboardGlyphManager.java
index f59d72b..6f19a3f 100644
--- a/services/core/java/com/android/server/input/KeyboardGlyphManager.java
+++ b/services/core/java/com/android/server/input/KeyboardGlyphManager.java
@@ -149,17 +149,17 @@
                 continue;
             }
             final ActivityInfo activityInfo = resolveInfo.activityInfo;
-            KeyGlyphMapData data = getKeyboardGlyphMapsInPackage(pm, activityInfo);
-            if (data == null) {
+            List<KeyGlyphMapData> data = getKeyboardGlyphMapsInPackage(pm, activityInfo);
+            if (data == null || data.isEmpty()) {
                 continue;
             }
-            glyphMaps.add(data);
+            glyphMaps.addAll(data);
         }
         return glyphMaps;
     }
 
     @Nullable
-    private KeyGlyphMapData getKeyboardGlyphMapsInPackage(PackageManager pm,
+    private List<KeyGlyphMapData> getKeyboardGlyphMapsInPackage(PackageManager pm,
             @NonNull ActivityInfo receiver) {
         Bundle metaData = receiver.metaData;
         if (metaData == null) {
@@ -175,6 +175,7 @@
 
         try {
             Resources resources = pm.getResourcesForApplication(receiver.applicationInfo);
+            List<KeyGlyphMapData> glyphMaps = new ArrayList<>();
             try (XmlResourceParser parser = resources.getXml(configResId)) {
                 XmlUtils.beginDocument(parser, TAG_KEYBOARD_GLYPH_MAPS);
 
@@ -193,13 +194,14 @@
                         int vendor = a.getInt(R.styleable.KeyboardGlyphMap_vendorId, -1);
                         int product = a.getInt(R.styleable.KeyboardGlyphMap_productId, -1);
                         if (glyphMapRes != 0 && vendor != -1 && product != -1) {
-                            return new KeyGlyphMapData(receiver.packageName, receiver.name,
-                                    glyphMapRes, vendor, product);
+                            glyphMaps.add(new KeyGlyphMapData(receiver.packageName, receiver.name,
+                                    glyphMapRes, vendor, product));
                         }
                     } finally {
                         a.recycle();
                     }
                 }
+                return glyphMaps;
             }
         } catch (Exception ex) {
             Slog.w(TAG, "Could not parse keyboard glyph map resource from receiver "
diff --git a/services/core/java/com/android/server/inputmethod/ImeBindingState.java b/services/core/java/com/android/server/inputmethod/ImeBindingState.java
index f78ea84..5deed39 100644
--- a/services/core/java/com/android/server/inputmethod/ImeBindingState.java
+++ b/services/core/java/com/android/server/inputmethod/ImeBindingState.java
@@ -20,6 +20,7 @@
 import static android.server.inputmethod.InputMethodManagerServiceProto.CUR_FOCUSED_WINDOW_SOFT_INPUT_MODE;
 import static android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
 import android.os.IBinder;
@@ -86,10 +87,10 @@
                 InputMethodDebug.softInputModeToString(mFocusedWindowSoftInputMode));
     }
 
-    void dump(String prefix, Printer p) {
-        p.println(prefix + "mFocusedWindow()=" + mFocusedWindow);
-        p.println(prefix + "softInputMode=" + InputMethodDebug.softInputModeToString(
-                mFocusedWindowSoftInputMode));
+    void dump(@NonNull Printer p, @NonNull String prefix) {
+        p.println(prefix + "mFocusedWindow=" + mFocusedWindow);
+        p.println(prefix + "mFocusedWindowSoftInputMode="
+                + InputMethodDebug.softInputModeToString(mFocusedWindowSoftInputMode));
         p.println(prefix + "mFocusedWindowClient=" + mFocusedWindowClient);
     }
 
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java b/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java
index ec1993a..477660d 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java
@@ -61,6 +61,7 @@
 import com.android.server.EventLogTags;
 import com.android.server.wm.WindowManagerInternal;
 
+import java.io.PrintWriter;
 import java.util.concurrent.CountDownLatch;
 
 /**
@@ -733,4 +734,25 @@
     void setBackDisposition(@BackDispositionMode int backDisposition) {
         mBackDisposition = backDisposition;
     }
+
+    @GuardedBy("ImfLock.class")
+    void dump(@NonNull PrintWriter pw, @NonNull String prefix) {
+        pw.println(prefix + "mSelectedMethodId=" + mSelectedMethodId);
+        pw.println(prefix + "mCurrentSubtype=" + mCurrentSubtype);
+        pw.println(prefix + "mCurSeq=" + mCurSeq);
+        pw.println(prefix + "mCurId=" + mCurId);
+        pw.println(prefix + "mHasMainConnection=" + mHasMainConnection);
+        pw.println(prefix + "mVisibleBound=" + mVisibleBound);
+        pw.println(prefix + "mCurToken=" + mCurToken);
+        pw.println(prefix + "mCurTokenDisplayId=" + mCurTokenDisplayId);
+        pw.println(prefix + "mCurHostInputToken=" + getCurHostInputToken());
+        pw.println(prefix + "mCurIntent=" + mCurIntent);
+        pw.println(prefix + "mCurMethod=" + mCurMethod);
+        pw.println(prefix + "mImeWindowVis=" + mImeWindowVis);
+        pw.println(prefix + "mBackDisposition=" + mBackDisposition);
+        pw.println(prefix + "mDisplayIdToShowIme=" + mDisplayIdToShowIme);
+        pw.println(prefix + "mDeviceIdToShowIme=" + mDeviceIdToShowIme);
+        pw.println(prefix + "mSupportsStylusHw=" + mSupportsStylusHw);
+        pw.println(prefix + "mSupportsConnectionlessStylusHw=" + mSupportsConnectionlessStylusHw);
+    }
 }
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 83044c2..0104373 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -1082,6 +1082,19 @@
             AdditionalSubtypeMapRepository.remove(userId);
             InputMethodSettingsRepository.remove(userId);
             mService.mUserDataRepository.remove(userId);
+            synchronized (ImfLock.class) {
+                final int nextOrCurrentUser = mService.mUserSwitchHandlerTask != null
+                        ? mService.mUserSwitchHandlerTask.mToUserId : mService.mCurrentImeUserId;
+                if (!mService.mConcurrentMultiUserModeEnabled && userId == nextOrCurrentUser) {
+                    // The current user was removed without an ongoing switch, or the user targeted
+                    // by the ongoing switch was removed. Switch to the current non-profile user
+                    // to allow starting input on it or one of its profile users later.
+                    // Note: non-profile users cannot be removed while they are the current user.
+                    final int currentUserId = mService.mActivityManagerInternal.getCurrentUserId();
+                    mService.scheduleSwitchUserTaskLocked(currentUserId,
+                            null /* clientToBeReset */);
+                }
+            }
         }
 
         @Override
@@ -1332,7 +1345,7 @@
                     + " prevUserId=" + prevUserId);
         }
 
-        // Clean up stuff for mCurrentUserId, which soon becomes the previous user.
+        // Clean up stuff for mCurrentImeUserId, which soon becomes the previous user.
 
         // TODO(b/338461930): Check if this is still necessary or not.
         onUnbindCurrentMethodByReset(prevUserId);
@@ -6063,42 +6076,38 @@
 
     @BinderThread
     @Override
-    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+    public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @Nullable String[] args) {
         if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
 
         PriorityDump.dump(mPriorityDumper, fd, pw, args);
     }
 
     @BinderThread
-    private void dumpAsStringNoCheck(FileDescriptor fd, PrintWriter pw, String[] args,
-            boolean isCritical) {
+    private void dumpAsStringNoCheck(@NonNull FileDescriptor fd, @NonNull PrintWriter pw,
+            @NonNull String[] args, boolean isCritical) {
         final int argUserId = parseUserIdFromDumpArgs(args);
         final Printer p = new PrintWriterPrinter(pw);
-        p.println("Current Input Method Manager state:");
+        p.println("Input Method Manager Service state:");
         p.println("  mSystemReady=" + mSystemReady);
         p.println("  mInteractive=" + mIsInteractive);
         p.println("  mConcurrentMultiUserModeEnabled=" + mConcurrentMultiUserModeEnabled);
-        p.println("  ENABLE_HIDE_IME_CAPTION_BAR="
-                + InputMethodService.ENABLE_HIDE_IME_CAPTION_BAR);
+        final int currentImeUserId;
         synchronized (ImfLock.class) {
+            currentImeUserId = mCurrentImeUserId;
+            p.println("  mCurrentImeUserId=" + currentImeUserId);
             p.println("  mStylusIds=" + (mStylusIds != null
                     ? Arrays.toString(mStylusIds.toArray()) : ""));
         }
+        // TODO(b/305849394): Make mMenuController multi-user aware.
         if (Flags.imeSwitcherRevamp()) {
             p.println("  mMenuControllerNew:");
-            mMenuControllerNew.dump(p, "  ");
+            mMenuControllerNew.dump(p, "    ");
         } else {
             p.println("  mMenuController:");
-            mMenuController.dump(p, "  ");
+            mMenuController.dump(p, "    ");
         }
-        if (mConcurrentMultiUserModeEnabled && argUserId == UserHandle.USER_NULL) {
-            mUserDataRepository.forAllUserData(
-                    u -> dumpAsStringNoCheckForUser(u, fd, pw, args, isCritical));
-        } else {
-            final int userId = argUserId != UserHandle.USER_NULL ? argUserId : mCurrentImeUserId;
-            final var userData = getUserData(userId);
-            dumpAsStringNoCheckForUser(userData, fd, pw, args, isCritical);
-        }
+        dumpClientController(p);
+        dumpUserRepository(p);
 
         // TODO(b/365868861): Make StartInputHistory and ImeTracker multi-user aware.
         synchronized (ImfLock.class) {
@@ -6112,12 +6121,18 @@
         p.println("  mImeTrackerService#History:");
         mImeTrackerService.dump(pw, "    ");
 
-        dumpUserRepository(p);
-        dumpClientStates(p);
+        if (mConcurrentMultiUserModeEnabled && argUserId == UserHandle.USER_NULL) {
+            mUserDataRepository.forAllUserData(
+                    u -> dumpAsStringNoCheckForUser(u, fd, pw, args, isCritical));
+        } else {
+            final int userId = argUserId != UserHandle.USER_NULL ? argUserId : currentImeUserId;
+            final var userData = getUserData(userId);
+            dumpAsStringNoCheckForUser(userData, fd, pw, args, isCritical);
+        }
     }
 
     @UserIdInt
-    private static int parseUserIdFromDumpArgs(String[] args) {
+    private static int parseUserIdFromDumpArgs(@NonNull String[] args) {
         final int userIdx = Arrays.binarySearch(args, "--user");
         if (userIdx == -1 || userIdx == args.length - 1) {
             return UserHandle.USER_NULL;
@@ -6127,44 +6142,37 @@
 
     // TODO(b/356239178): Update dump format output to better group per-user info.
     @BinderThread
-    private void dumpAsStringNoCheckForUser(UserData userData, FileDescriptor fd, PrintWriter pw,
-            String[] args, boolean isCritical) {
+    private void dumpAsStringNoCheckForUser(@NonNull UserData userData, @NonNull FileDescriptor fd,
+            @NonNull PrintWriter pw, @NonNull String[] args, boolean isCritical) {
         final Printer p = new PrintWriterPrinter(pw);
-        IInputMethodInvoker method;
         ClientState client;
+        IInputMethodInvoker method;
         p.println("  UserId=" + userData.mUserId);
         synchronized (ImfLock.class) {
-            final InputMethodSettings settings = InputMethodSettingsRepository.get(
-                    userData.mUserId);
+            final var bindingController = userData.mBindingController;
+            client = userData.mCurClient;
+            method = bindingController.getCurMethod();
+            p.println("    mBindingController:");
+            bindingController.dump(pw, "      ");
+            p.println("    mCurClient=" + client);
+            p.println("    mFocusedWindowPerceptible=" + mFocusedWindowPerceptible);
+            p.println("    mImeBindingState:");
+            userData.mImeBindingState.dump(p, "      ");
+            p.println("    mBoundToMethod=" + userData.mBoundToMethod);
+            p.println("    mEnabledSession=" + userData.mEnabledSession);
+            p.println("    mVisibilityStateComputer:");
+            userData.mVisibilityStateComputer.dump(pw, "      ");
+            p.println("    mInFullscreenMode=" + userData.mInFullscreenMode);
+
+            final var settings = InputMethodSettingsRepository.get(userData.mUserId);
             final List<InputMethodInfo> methodList = settings.getMethodList();
-            int numImes = methodList.size();
+            final int numImes = methodList.size();
             p.println("    Input Methods:");
             for (int i = 0; i < numImes; i++) {
-                InputMethodInfo info = methodList.get(i);
+                final InputMethodInfo info = methodList.get(i);
                 p.println("      InputMethod #" + i + ":");
                 info.dump(p, "        ");
             }
-            final var bindingController = userData.mBindingController;
-            p.println("        mCurMethodId=" + bindingController.getSelectedMethodId());
-            client = userData.mCurClient;
-            p.println("        mCurClient=" + client + " mCurSeq="
-                    + bindingController.getSequenceNumber());
-            p.println("        mFocusedWindowPerceptible=" + mFocusedWindowPerceptible);
-            userData.mImeBindingState.dump(/* prefix= */ "  ", p);
-            p.println("        mCurId=" + bindingController.getCurId());
-            p.println("        mHaveConnection=" + bindingController.hasMainConnection());
-            p.println("        mBoundToMethod=" + userData.mBoundToMethod);
-            p.println("        mVisibleBound=" + bindingController.isVisibleBound());
-            p.println("        mCurToken=" + bindingController.getCurToken());
-            p.println("        mCurTokenDisplayId=" + bindingController.getCurTokenDisplayId());
-            p.println("        mCurHostInputToken=" + bindingController.getCurHostInputToken());
-            p.println("        mCurIntent=" + bindingController.getCurIntent());
-            method = bindingController.getCurMethod();
-            p.println("        mCurMethod=" + method);
-            p.println("        mEnabledSession=" + userData.mEnabledSession);
-            final var visibilityStateComputer = userData.mVisibilityStateComputer;
-            visibilityStateComputer.dump(pw, "  ");
-            p.println("        mInFullscreenMode=" + userData.mInFullscreenMode);
         }
 
         // Exit here for critical dump, as remaining sections require IPCs to other processes.
@@ -6172,7 +6180,7 @@
             return;
         }
 
-        p.println(" ");
+        p.println("");
         if (client != null) {
             pw.flush();
             try {
@@ -6184,25 +6192,23 @@
             p.println("No input method client.");
         }
         synchronized (ImfLock.class) {
-            if (userData.mImeBindingState.mFocusedWindowClient != null
-                    && client != userData.mImeBindingState.mFocusedWindowClient) {
-                p.println(" ");
-                p.println("Warning: Current input method client doesn't match the last focused. "
-                        + "window.");
+            final var focusedWindowClient = userData.mImeBindingState.mFocusedWindowClient;
+            if (focusedWindowClient != null && client != focusedWindowClient) {
+                p.println("");
+                p.println("Warning: Current input method client doesn't match the last focused"
+                        + " window.");
                 p.println("Dumping input method client in the last focused window just in case.");
-                p.println(" ");
+                p.println("");
                 pw.flush();
                 try {
-                    TransferPipe.dumpAsync(
-                            userData.mImeBindingState.mFocusedWindowClient.mClient.asBinder(), fd,
-                            args);
+                    TransferPipe.dumpAsync(focusedWindowClient.mClient.asBinder(), fd, args);
                 } catch (IOException | RemoteException e) {
                     p.println("Failed to dump input method client in focused window: " + e);
                 }
             }
         }
 
-        p.println(" ");
+        p.println("");
         if (method != null) {
             pw.flush();
             try {
@@ -6215,56 +6221,51 @@
         }
     }
 
-    private void dumpClientStates(Printer p) {
-        p.println(" ClientStates:");
+    private void dumpClientController(@NonNull Printer p) {
+        p.println("  mClientController:");
         // TODO(b/324907325): Remove the suppress warnings once b/324907325 is fixed.
         @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("    " + c + ":");
+            p.println("      client=" + c.mClient);
+            p.println("      fallbackInputConnection=" + c.mFallbackInputConnection);
+            p.println("      sessionRequested=" + c.mSessionRequested);
+            p.println("      sessionRequestedForAccessibility="
                     + c.mSessionRequestedForAccessibility);
-            p.println("    curSession=" + c.mCurSession);
-            p.println("    selfReportedDisplayId=" + c.mSelfReportedDisplayId);
-            p.println("    uid=" + c.mUid);
-            p.println("    pid=" + c.mPid);
+            p.println("      curSession=" + c.mCurSession);
+            p.println("      selfReportedDisplayId=" + c.mSelfReportedDisplayId);
+            p.println("      uid=" + c.mUid);
+            p.println("      pid=" + c.mPid);
         };
         synchronized (ImfLock.class) {
             mClientController.forAllClients(clientControllerDump);
         }
     }
 
-    private void dumpUserRepository(Printer p) {
-        p.println("  mUserDataRepository=");
+    private void dumpUserRepository(@NonNull Printer p) {
+        p.println("  mUserDataRepository:");
         // TODO(b/324907325): Remove the suppress warnings once b/324907325 is fixed.
-        @SuppressWarnings("GuardedBy") Consumer<UserData> userDataDump =
-                u -> {
-                    p.println("    mUserId=" + u.mUserId);
-                    p.println("      unlocked=" + u.mIsUnlockingOrUnlocked.get());
-                    p.println("      hasMainConnection="
-                            + u.mBindingController.hasMainConnection());
-                    p.println("      isVisibleBound=" + u.mBindingController.isVisibleBound());
-                    p.println("      boundToMethod=" + u.mBoundToMethod);
-                    p.println("      curClient=" + u.mCurClient);
-                    if (u.mCurEditorInfo != null) {
-                        p.println("      curEditorInfo:");
-                        u.mCurEditorInfo.dump(p, "        ", false /* dumpExtras */);
-                    } else {
-                        p.println("      curEditorInfo: null");
-                    }
-                    p.println("      imeBindingState:");
-                    u.mImeBindingState.dump("        ", p);
-                    p.println("      enabledSession=" + u.mEnabledSession);
-                    p.println("      inFullscreenMode=" + u.mInFullscreenMode);
-                    p.println("      imeDrawsNavBar=" + u.mImeDrawsNavBar.get());
-                    p.println("      switchingController:");
-                    u.mSwitchingController.dump(p, "        ");
-                    p.println("      mLastEnabledInputMethodsStr="
-                            + u.mLastEnabledInputMethodsStr);
-                };
+        @SuppressWarnings("GuardedBy") Consumer<UserData> userDataDump = u -> {
+            p.println("    userId=" + u.mUserId);
+            p.println("      unlocked=" + u.mIsUnlockingOrUnlocked.get());
+            p.println("      hasMainConnection=" + u.mBindingController.hasMainConnection());
+            p.println("      isVisibleBound=" + u.mBindingController.isVisibleBound());
+            p.println("      boundToMethod=" + u.mBoundToMethod);
+            p.println("      curClient=" + u.mCurClient);
+            if (u.mCurEditorInfo != null) {
+                p.println("      curEditorInfo:");
+                u.mCurEditorInfo.dump(p, "        ", false /* dumpExtras */);
+            } else {
+                p.println("      curEditorInfo: null");
+            }
+            p.println("      imeBindingState:");
+            u.mImeBindingState.dump(p, "        ");
+            p.println("      enabledSession=" + u.mEnabledSession);
+            p.println("      inFullscreenMode=" + u.mInFullscreenMode);
+            p.println("      imeDrawsNavBar=" + u.mImeDrawsNavBar.get());
+            p.println("      switchingController:");
+            u.mSwitchingController.dump(p, "        ");
+            p.println("      mLastEnabledInputMethodsStr=" + u.mLastEnabledInputMethodsStr);
+        };
         synchronized (ImfLock.class) {
             mUserDataRepository.forAllUserData(userDataDump);
         }
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java b/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java
index b5ee068..248fa60 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java
@@ -287,10 +287,10 @@
 
     void dump(@NonNull Printer pw, @NonNull String prefix) {
         final boolean showing = isisInputMethodPickerShownForTestLocked();
-        pw.println(prefix + "  isShowing: " + showing);
+        pw.println(prefix + "isShowing: " + showing);
 
         if (showing) {
-            pw.println(prefix + "  imList: " + mImList);
+            pw.println(prefix + "imList: " + mImList);
         }
     }
 
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodMenuControllerNew.java b/services/core/java/com/android/server/inputmethod/InputMethodMenuControllerNew.java
index 1d0e3c6..6abd5aa 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodMenuControllerNew.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodMenuControllerNew.java
@@ -187,10 +187,10 @@
 
     void dump(@NonNull Printer pw, @NonNull String prefix) {
         final boolean showing = isShowing();
-        pw.println(prefix + "  isShowing: " + showing);
+        pw.println(prefix + "isShowing: " + showing);
 
         if (showing) {
-            pw.println(prefix + "  menuItems: " + mMenuItems);
+            pw.println(prefix + "menuItems: " + mMenuItems);
         }
     }
 
diff --git a/services/core/java/com/android/server/integrity/parser/BinaryFileOperations.java b/services/core/java/com/android/server/integrity/parser/BinaryFileOperations.java
deleted file mode 100644
index f09e035e..0000000
--- a/services/core/java/com/android/server/integrity/parser/BinaryFileOperations.java
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.integrity.parser;
-
-import static com.android.server.integrity.model.ComponentBitSize.IS_HASHED_BITS;
-import static com.android.server.integrity.model.ComponentBitSize.VALUE_SIZE_BITS;
-
-import android.content.integrity.IntegrityUtils;
-
-import com.android.server.integrity.model.BitInputStream;
-
-import java.io.IOException;
-import java.nio.ByteBuffer;
-
-/**
- * Helper methods for reading standard data structures from {@link BitInputStream}.
- */
-public class BinaryFileOperations {
-
-    /**
-     * Read an string value with the given size and hash status from a {@code BitInputStream}.
-     *
-     * If the value is hashed, get the hex-encoding of the value. Serialized values are in raw form.
-     * All hashed values are hex-encoded.
-     */
-    public static String getStringValue(BitInputStream bitInputStream) throws IOException {
-        boolean isHashedValue = bitInputStream.getNext(IS_HASHED_BITS) == 1;
-        int valueSize = bitInputStream.getNext(VALUE_SIZE_BITS);
-        return getStringValue(bitInputStream, valueSize, isHashedValue);
-    }
-
-    /**
-     * Read an string value with the given size and hash status from a {@code BitInputStream}.
-     *
-     * If the value is hashed, get the hex-encoding of the value. Serialized values are in raw form.
-     * All hashed values are hex-encoded.
-     */
-    public static String getStringValue(
-            BitInputStream bitInputStream, int valueSize, boolean isHashedValue)
-            throws IOException {
-        if (!isHashedValue) {
-            StringBuilder value = new StringBuilder();
-            while (valueSize-- > 0) {
-                value.append((char) bitInputStream.getNext(/* numOfBits= */ 8));
-            }
-            return value.toString();
-        }
-        ByteBuffer byteBuffer = ByteBuffer.allocate(valueSize);
-        while (valueSize-- > 0) {
-            byteBuffer.put((byte) (bitInputStream.getNext(/* numOfBits= */ 8) & 0xFF));
-        }
-        return IntegrityUtils.getHexDigest(byteBuffer.array());
-    }
-
-    /** Read an integer value from a {@code BitInputStream}. */
-    public static int getIntValue(BitInputStream bitInputStream) throws IOException {
-        return bitInputStream.getNext(/* numOfBits= */ 32);
-    }
-
-    /** Read an boolean value from a {@code BitInputStream}. */
-    public static boolean getBooleanValue(BitInputStream bitInputStream) throws IOException {
-        return bitInputStream.getNext(/* numOfBits= */ 1) == 1;
-    }
-}
diff --git a/services/core/java/com/android/server/integrity/parser/LimitInputStream.java b/services/core/java/com/android/server/integrity/parser/LimitInputStream.java
deleted file mode 100644
index a91bbb7..0000000
--- a/services/core/java/com/android/server/integrity/parser/LimitInputStream.java
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.integrity.parser;
-
-import java.io.FilterInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-
-/** An {@link InputStream} that basically truncates another {@link InputStream} */
-public class LimitInputStream extends FilterInputStream {
-    private int mReadBytes;
-    private final int mLimit;
-
-    public LimitInputStream(InputStream in, int limit) {
-        super(in);
-        if (limit < 0) {
-            throw new IllegalArgumentException("limit " + limit + " cannot be negative");
-        }
-        mReadBytes = 0;
-        mLimit = limit;
-    }
-
-    @Override
-    public int available() throws IOException {
-        return Math.min(super.available(), mLimit - mReadBytes);
-    }
-
-    @Override
-    public int read() throws IOException {
-        if (mReadBytes == mLimit) {
-            return -1;
-        }
-        mReadBytes++;
-        return super.read();
-    }
-
-    @Override
-    public int read(byte[] b) throws IOException {
-        return read(b, 0, b.length);
-    }
-
-    @Override
-    public int read(byte[] b, int off, int len) throws IOException {
-        if (len <= 0) {
-            return 0;
-        }
-        int available = available();
-        if (available <= 0) {
-            return -1;
-        }
-        int result = super.read(b, off, Math.min(len, available));
-        mReadBytes += result;
-        return result;
-    }
-
-    @Override
-    public long skip(long n) throws IOException {
-        if (n <= 0) {
-            return 0;
-        }
-        int available = available();
-        if (available <= 0) {
-            return 0;
-        }
-        int bytesToSkip = (int) Math.min(available, n);
-        long bytesSkipped = super.skip(bytesToSkip);
-        mReadBytes += (int) bytesSkipped;
-        return bytesSkipped;
-    }
-}
diff --git a/services/core/java/com/android/server/integrity/parser/RandomAccessInputStream.java b/services/core/java/com/android/server/integrity/parser/RandomAccessInputStream.java
deleted file mode 100644
index 206e6a1..0000000
--- a/services/core/java/com/android/server/integrity/parser/RandomAccessInputStream.java
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.integrity.parser;
-
-import java.io.IOException;
-import java.io.InputStream;
-
-/** A wrapper around {@link RandomAccessObject} to turn it into a {@link InputStream}. */
-public class RandomAccessInputStream extends InputStream {
-
-    private final RandomAccessObject mRandomAccessObject;
-
-    private int mPosition;
-
-    public RandomAccessInputStream(RandomAccessObject object) throws IOException {
-        mRandomAccessObject = object;
-        mPosition = 0;
-    }
-
-    /** Returns the position of the file pointer. */
-    public int getPosition() {
-        return mPosition;
-    }
-
-    /** See {@link RandomAccessObject#seek(int)} */
-    public void seek(int position) throws IOException {
-        mRandomAccessObject.seek(position);
-        mPosition = position;
-    }
-
-    @Override
-    public int available() throws IOException {
-        return mRandomAccessObject.length() - mPosition;
-    }
-
-    @Override
-    public void close() throws IOException {
-        mRandomAccessObject.close();
-    }
-
-    @Override
-    public int read() throws IOException {
-        if (available() <= 0) {
-            return -1;
-        }
-        mPosition++;
-        return mRandomAccessObject.read();
-    }
-
-    @Override
-    public int read(byte[] b) throws IOException {
-        return read(b, 0, b.length);
-    }
-
-    @Override
-    public int read(byte[] b, int off, int len) throws IOException {
-        if (len <= 0) {
-            return 0;
-        }
-        int available = available();
-        if (available <= 0) {
-            return -1;
-        }
-        int result = mRandomAccessObject.read(b, off, Math.min(len, available));
-        mPosition += result;
-        return result;
-    }
-
-    @Override
-    public long skip(long n) throws IOException {
-        if (n <= 0) {
-            return 0;
-        }
-        int available = available();
-        if (available <= 0) {
-            return 0;
-        }
-        int skipAmount = (int) Math.min(available, n);
-        mPosition += skipAmount;
-        mRandomAccessObject.seek(mPosition);
-        return skipAmount;
-    }
-}
diff --git a/services/core/java/com/android/server/integrity/parser/RandomAccessObject.java b/services/core/java/com/android/server/integrity/parser/RandomAccessObject.java
deleted file mode 100644
index d9b2e38..0000000
--- a/services/core/java/com/android/server/integrity/parser/RandomAccessObject.java
+++ /dev/null
@@ -1,133 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.integrity.parser;
-
-import java.io.File;
-import java.io.IOException;
-import java.io.RandomAccessFile;
-import java.nio.ByteBuffer;
-
-/** An interface for random access objects like RandomAccessFile or byte arrays. */
-public abstract class RandomAccessObject {
-
-    /** See {@link RandomAccessFile#seek(long)}. */
-    public abstract void seek(int position) throws IOException;
-
-    /** See {@link RandomAccessFile#read()}. */
-    public abstract int read() throws IOException;
-
-    /** See {@link RandomAccessFile#read(byte[], int, int)}. */
-    public abstract int read(byte[] bytes, int off, int len) throws IOException;
-
-    /** See {@link RandomAccessFile#close()}. */
-    public abstract void close() throws IOException;
-
-    /** See {@link java.io.RandomAccessFile#length()}. */
-    public abstract int length();
-
-    /** Static constructor from a file. */
-    public static RandomAccessObject ofFile(File file) throws IOException {
-        return new RandomAccessFileObject(file);
-    }
-
-    /** Static constructor from a byte array. */
-    public static RandomAccessObject ofBytes(byte[] bytes) {
-        return new RandomAccessByteArrayObject(bytes);
-    }
-
-    private static class RandomAccessFileObject extends RandomAccessObject {
-        private final RandomAccessFile mRandomAccessFile;
-        // We cache the length since File.length() invokes file IO.
-        private final int mLength;
-
-        RandomAccessFileObject(File file) throws IOException {
-            long length = file.length();
-            if (length > Integer.MAX_VALUE) {
-                throw new IOException("Unsupported file size (too big) " + length);
-            }
-
-            mRandomAccessFile = new RandomAccessFile(file, /* mode= */ "r");
-            mLength = (int) length;
-        }
-
-        @Override
-        public void seek(int position) throws IOException {
-            mRandomAccessFile.seek(position);
-        }
-
-        @Override
-        public int read() throws IOException {
-            return mRandomAccessFile.read();
-        }
-
-        @Override
-        public int read(byte[] bytes, int off, int len) throws IOException {
-            return mRandomAccessFile.read(bytes, off, len);
-        }
-
-        @Override
-        public void close() throws IOException {
-            mRandomAccessFile.close();
-        }
-
-        @Override
-        public int length() {
-            return mLength;
-        }
-    }
-
-    private static class RandomAccessByteArrayObject extends RandomAccessObject {
-
-        private final ByteBuffer mBytes;
-
-        RandomAccessByteArrayObject(byte[] bytes) {
-            mBytes = ByteBuffer.wrap(bytes);
-        }
-
-        @Override
-        public void seek(int position) throws IOException {
-            mBytes.position(position);
-        }
-
-        @Override
-        public int read() throws IOException {
-            if (!mBytes.hasRemaining()) {
-                return -1;
-            }
-
-            return mBytes.get() & 0xFF;
-        }
-
-        @Override
-        public int read(byte[] bytes, int off, int len) throws IOException {
-            int bytesToCopy = Math.min(len, mBytes.remaining());
-            if (bytesToCopy <= 0) {
-                return 0;
-            }
-            mBytes.get(bytes, off, len);
-            return bytesToCopy;
-        }
-
-        @Override
-        public void close() throws IOException {}
-
-        @Override
-        public int length() {
-            return mBytes.capacity();
-        }
-    }
-}
diff --git a/services/core/java/com/android/server/integrity/parser/RuleBinaryParser.java b/services/core/java/com/android/server/integrity/parser/RuleBinaryParser.java
deleted file mode 100644
index ea3a3d5..0000000
--- a/services/core/java/com/android/server/integrity/parser/RuleBinaryParser.java
+++ /dev/null
@@ -1,193 +0,0 @@
-/*
- * Copyright (C) 2019 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.integrity.parser;
-
-import static com.android.server.integrity.model.ComponentBitSize.ATOMIC_FORMULA_START;
-import static com.android.server.integrity.model.ComponentBitSize.BYTE_BITS;
-import static com.android.server.integrity.model.ComponentBitSize.COMPOUND_FORMULA_END;
-import static com.android.server.integrity.model.ComponentBitSize.COMPOUND_FORMULA_START;
-import static com.android.server.integrity.model.ComponentBitSize.CONNECTOR_BITS;
-import static com.android.server.integrity.model.ComponentBitSize.EFFECT_BITS;
-import static com.android.server.integrity.model.ComponentBitSize.FORMAT_VERSION_BITS;
-import static com.android.server.integrity.model.ComponentBitSize.INSTALLER_ALLOWED_BY_MANIFEST_START;
-import static com.android.server.integrity.model.ComponentBitSize.IS_HASHED_BITS;
-import static com.android.server.integrity.model.ComponentBitSize.KEY_BITS;
-import static com.android.server.integrity.model.ComponentBitSize.OPERATOR_BITS;
-import static com.android.server.integrity.model.ComponentBitSize.SEPARATOR_BITS;
-import static com.android.server.integrity.model.ComponentBitSize.SIGNAL_BIT;
-import static com.android.server.integrity.model.ComponentBitSize.VALUE_SIZE_BITS;
-import static com.android.server.integrity.parser.BinaryFileOperations.getBooleanValue;
-import static com.android.server.integrity.parser.BinaryFileOperations.getIntValue;
-import static com.android.server.integrity.parser.BinaryFileOperations.getStringValue;
-
-import android.content.integrity.AtomicFormula;
-import android.content.integrity.CompoundFormula;
-import android.content.integrity.InstallerAllowedByManifestFormula;
-import android.content.integrity.IntegrityFormula;
-import android.content.integrity.Rule;
-
-import com.android.server.integrity.model.BitInputStream;
-
-import java.io.BufferedInputStream;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-/** A helper class to parse rules into the {@link Rule} model from Binary representation. */
-public class RuleBinaryParser implements RuleParser {
-
-    @Override
-    public List<Rule> parse(byte[] ruleBytes) throws RuleParseException {
-        return parse(RandomAccessObject.ofBytes(ruleBytes), Collections.emptyList());
-    }
-
-    @Override
-    public List<Rule> parse(RandomAccessObject randomAccessObject, List<RuleIndexRange> indexRanges)
-            throws RuleParseException {
-        try (RandomAccessInputStream randomAccessInputStream =
-                new RandomAccessInputStream(randomAccessObject)) {
-            return parseRules(randomAccessInputStream, indexRanges);
-        } catch (Exception e) {
-            throw new RuleParseException(e.getMessage(), e);
-        }
-    }
-
-    private List<Rule> parseRules(
-            RandomAccessInputStream randomAccessInputStream, List<RuleIndexRange> indexRanges)
-            throws IOException {
-
-        // Read the rule binary file format version.
-        randomAccessInputStream.skip(FORMAT_VERSION_BITS / BYTE_BITS);
-
-        return indexRanges.isEmpty()
-                ? parseAllRules(randomAccessInputStream)
-                : parseIndexedRules(randomAccessInputStream, indexRanges);
-    }
-
-    private List<Rule> parseAllRules(RandomAccessInputStream randomAccessInputStream)
-            throws IOException {
-        List<Rule> parsedRules = new ArrayList<>();
-
-        BitInputStream inputStream =
-                new BitInputStream(new BufferedInputStream(randomAccessInputStream));
-        while (inputStream.hasNext()) {
-            if (inputStream.getNext(SIGNAL_BIT) == 1) {
-                parsedRules.add(parseRule(inputStream));
-            }
-        }
-
-        return parsedRules;
-    }
-
-    private List<Rule> parseIndexedRules(
-            RandomAccessInputStream randomAccessInputStream, List<RuleIndexRange> indexRanges)
-            throws IOException {
-        List<Rule> parsedRules = new ArrayList<>();
-
-        for (RuleIndexRange range : indexRanges) {
-            randomAccessInputStream.seek(range.getStartIndex());
-
-            BitInputStream inputStream =
-                    new BitInputStream(
-                            new BufferedInputStream(
-                                    new LimitInputStream(
-                                            randomAccessInputStream,
-                                            range.getEndIndex() - range.getStartIndex())));
-
-            // Read the rules until we reach the end index. available() here is not reliable.
-            while (inputStream.hasNext()) {
-                if (inputStream.getNext(SIGNAL_BIT) == 1) {
-                    parsedRules.add(parseRule(inputStream));
-                }
-            }
-        }
-
-        return parsedRules;
-    }
-
-    private Rule parseRule(BitInputStream bitInputStream) throws IOException {
-        IntegrityFormula formula = parseFormula(bitInputStream);
-        int effect = bitInputStream.getNext(EFFECT_BITS);
-
-        if (bitInputStream.getNext(SIGNAL_BIT) != 1) {
-            throw new IllegalArgumentException("A rule must end with a '1' bit.");
-        }
-
-        return new Rule(formula, effect);
-    }
-
-    private IntegrityFormula parseFormula(BitInputStream bitInputStream) throws IOException {
-        int separator = bitInputStream.getNext(SEPARATOR_BITS);
-        switch (separator) {
-            case ATOMIC_FORMULA_START:
-                return parseAtomicFormula(bitInputStream);
-            case COMPOUND_FORMULA_START:
-                return parseCompoundFormula(bitInputStream);
-            case COMPOUND_FORMULA_END:
-                return null;
-            case INSTALLER_ALLOWED_BY_MANIFEST_START:
-                return new InstallerAllowedByManifestFormula();
-            default:
-                throw new IllegalArgumentException(
-                        String.format("Unknown formula separator: %s", separator));
-        }
-    }
-
-    private CompoundFormula parseCompoundFormula(BitInputStream bitInputStream) throws IOException {
-        int connector = bitInputStream.getNext(CONNECTOR_BITS);
-        List<IntegrityFormula> formulas = new ArrayList<>();
-
-        IntegrityFormula parsedFormula = parseFormula(bitInputStream);
-        while (parsedFormula != null) {
-            formulas.add(parsedFormula);
-            parsedFormula = parseFormula(bitInputStream);
-        }
-
-        return new CompoundFormula(connector, formulas);
-    }
-
-    private AtomicFormula parseAtomicFormula(BitInputStream bitInputStream) throws IOException {
-        int key = bitInputStream.getNext(KEY_BITS);
-        int operator = bitInputStream.getNext(OPERATOR_BITS);
-
-        switch (key) {
-            case AtomicFormula.PACKAGE_NAME:
-            case AtomicFormula.APP_CERTIFICATE:
-            case AtomicFormula.APP_CERTIFICATE_LINEAGE:
-            case AtomicFormula.INSTALLER_NAME:
-            case AtomicFormula.INSTALLER_CERTIFICATE:
-            case AtomicFormula.STAMP_CERTIFICATE_HASH:
-                boolean isHashedValue = bitInputStream.getNext(IS_HASHED_BITS) == 1;
-                int valueSize = bitInputStream.getNext(VALUE_SIZE_BITS);
-                String stringValue = getStringValue(bitInputStream, valueSize, isHashedValue);
-                return new AtomicFormula.StringAtomicFormula(key, stringValue, isHashedValue);
-            case AtomicFormula.VERSION_CODE:
-                // TODO(b/147880712): temporary hack until our input handles long
-                long upper = getIntValue(bitInputStream);
-                long lower = getIntValue(bitInputStream);
-                long longValue = (upper << 32) | lower;
-                return new AtomicFormula.LongAtomicFormula(key, operator, longValue);
-            case AtomicFormula.PRE_INSTALLED:
-            case AtomicFormula.STAMP_TRUSTED:
-                boolean booleanValue = getBooleanValue(bitInputStream);
-                return new AtomicFormula.BooleanAtomicFormula(key, booleanValue);
-            default:
-                throw new IllegalArgumentException(String.format("Unknown key: %d", key));
-        }
-    }
-}
diff --git a/services/core/java/com/android/server/integrity/parser/RuleIndexRange.java b/services/core/java/com/android/server/integrity/parser/RuleIndexRange.java
deleted file mode 100644
index 595a035..0000000
--- a/services/core/java/com/android/server/integrity/parser/RuleIndexRange.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.integrity.parser;
-
-import android.annotation.Nullable;
-
-/**
- * A wrapper class to represent an indexing range that is identified by the {@link
- * RuleIndexingController}.
- */
-public class RuleIndexRange {
-    private int mStartIndex;
-    private int mEndIndex;
-
-    /** Constructor with start and end indexes. */
-    public RuleIndexRange(int startIndex, int endIndex) {
-        this.mStartIndex = startIndex;
-        this.mEndIndex = endIndex;
-    }
-
-    /** Returns the startIndex. */
-    public int getStartIndex() {
-        return mStartIndex;
-    }
-
-    /** Returns the end index. */
-    public int getEndIndex() {
-        return mEndIndex;
-    }
-
-    @Override
-    public boolean equals(@Nullable Object object) {
-        return mStartIndex == ((RuleIndexRange) object).getStartIndex()
-                && mEndIndex == ((RuleIndexRange) object).getEndIndex();
-    }
-
-    @Override
-    public String toString() {
-        return String.format("Range{%d, %d}", mStartIndex, mEndIndex);
-    }
-}
diff --git a/services/core/java/com/android/server/integrity/parser/RuleIndexingController.java b/services/core/java/com/android/server/integrity/parser/RuleIndexingController.java
deleted file mode 100644
index 348a03b..0000000
--- a/services/core/java/com/android/server/integrity/parser/RuleIndexingController.java
+++ /dev/null
@@ -1,128 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.integrity.parser;
-
-import static com.android.server.integrity.model.IndexingFileConstants.END_INDEXING_KEY;
-import static com.android.server.integrity.model.IndexingFileConstants.START_INDEXING_KEY;
-import static com.android.server.integrity.parser.BinaryFileOperations.getIntValue;
-import static com.android.server.integrity.parser.BinaryFileOperations.getStringValue;
-
-import android.content.integrity.AppInstallMetadata;
-
-import com.android.server.integrity.model.BitInputStream;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.stream.Collectors;
-
-/** Helper class to identify the necessary indexes that needs to be read. */
-public class RuleIndexingController {
-
-    private static LinkedHashMap<String, Integer> sPackageNameBasedIndexes;
-    private static LinkedHashMap<String, Integer> sAppCertificateBasedIndexes;
-    private static LinkedHashMap<String, Integer> sUnindexedRuleIndexes;
-
-    /**
-     * Provide the indexing file to read and the object will be constructed by reading and
-     * identifying the indexes.
-     */
-    public RuleIndexingController(InputStream inputStream) throws IOException {
-        BitInputStream bitInputStream = new BitInputStream(inputStream);
-        sPackageNameBasedIndexes = getNextIndexGroup(bitInputStream);
-        sAppCertificateBasedIndexes = getNextIndexGroup(bitInputStream);
-        sUnindexedRuleIndexes = getNextIndexGroup(bitInputStream);
-    }
-
-    /**
-     * Returns a list of integers with the starting and ending bytes of the rules that needs to be
-     * read and evaluated.
-     */
-    public List<RuleIndexRange> identifyRulesToEvaluate(AppInstallMetadata appInstallMetadata) {
-        List<RuleIndexRange> indexRanges = new ArrayList<>();
-
-        // Add the range for package name indexes rules.
-        indexRanges.add(
-                searchIndexingKeysRangeContainingKey(
-                        sPackageNameBasedIndexes, appInstallMetadata.getPackageName()));
-
-        // Add the range for app certificate indexes rules of all certificates.
-        for (String appCertificate : appInstallMetadata.getAppCertificates()) {
-            indexRanges.add(
-                    searchIndexingKeysRangeContainingKey(
-                            sAppCertificateBasedIndexes, appCertificate));
-        }
-
-        // Add the range for unindexed rules.
-        indexRanges.add(
-                new RuleIndexRange(
-                        sUnindexedRuleIndexes.get(START_INDEXING_KEY),
-                        sUnindexedRuleIndexes.get(END_INDEXING_KEY)));
-
-        return indexRanges;
-    }
-
-    private LinkedHashMap<String, Integer> getNextIndexGroup(BitInputStream bitInputStream)
-            throws IOException {
-        LinkedHashMap<String, Integer> keyToIndexMap = new LinkedHashMap<>();
-        while (bitInputStream.hasNext()) {
-            String key = getStringValue(bitInputStream);
-            int value = getIntValue(bitInputStream);
-
-            keyToIndexMap.put(key, value);
-
-            if (key.matches(END_INDEXING_KEY)) {
-                break;
-            }
-        }
-        if (keyToIndexMap.size() < 2) {
-            throw new IllegalStateException("Indexing file is corrupt.");
-        }
-        return keyToIndexMap;
-    }
-
-    private static RuleIndexRange searchIndexingKeysRangeContainingKey(
-            LinkedHashMap<String, Integer> indexMap, String searchedKey) {
-        List<String> keys = indexMap.keySet().stream().collect(Collectors.toList());
-        List<String> identifiedKeyRange =
-                searchKeysRangeContainingKey(keys, searchedKey, 0, keys.size() - 1);
-        return new RuleIndexRange(
-                indexMap.get(identifiedKeyRange.get(0)), indexMap.get(identifiedKeyRange.get(1)));
-    }
-
-    private static List<String> searchKeysRangeContainingKey(
-            List<String> sortedKeyList, String key, int startIndex, int endIndex) {
-        if (endIndex <= startIndex) {
-            throw new IllegalStateException("Indexing file is corrupt.");
-        }
-        if (endIndex - startIndex == 1) {
-            return Arrays.asList(sortedKeyList.get(startIndex), sortedKeyList.get(endIndex));
-        }
-
-        int midKeyIndex = startIndex + ((endIndex - startIndex) / 2);
-        String midKey = sortedKeyList.get(midKeyIndex);
-
-        if (key.compareTo(midKey) >= 0) {
-            return searchKeysRangeContainingKey(sortedKeyList, key, midKeyIndex, endIndex);
-        } else {
-            return searchKeysRangeContainingKey(sortedKeyList, key, startIndex, midKeyIndex);
-        }
-    }
-}
diff --git a/services/core/java/com/android/server/integrity/parser/RuleParseException.java b/services/core/java/com/android/server/integrity/parser/RuleParseException.java
deleted file mode 100644
index c0f36a6..0000000
--- a/services/core/java/com/android/server/integrity/parser/RuleParseException.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright (C) 2019 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.integrity.parser;
-
-import android.annotation.NonNull;
-
-/**
- * Thrown when rule parsing fails.
- */
-public class RuleParseException extends Exception {
-    public RuleParseException(@NonNull String message) {
-        super(message);
-    }
-
-    public RuleParseException(@NonNull String message, @NonNull Throwable cause) {
-        super(message, cause);
-    }
-}
diff --git a/services/core/java/com/android/server/integrity/parser/RuleParser.java b/services/core/java/com/android/server/integrity/parser/RuleParser.java
deleted file mode 100644
index 126dacc..0000000
--- a/services/core/java/com/android/server/integrity/parser/RuleParser.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright (C) 2019 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.integrity.parser;
-
-import android.content.integrity.Rule;
-
-import java.util.List;
-
-/** A helper class to parse rules into the {@link Rule} model. */
-public interface RuleParser {
-
-    /** Parse rules from bytes. */
-    List<Rule> parse(byte[] ruleBytes) throws RuleParseException;
-
-    /** Parse rules from an input stream. */
-    List<Rule> parse(RandomAccessObject randomAccessObject, List<RuleIndexRange> ruleIndexRanges)
-            throws RuleParseException;
-}
diff --git a/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java b/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
index e0913cc..436acba 100644
--- a/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
+++ b/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
@@ -660,8 +660,13 @@
 
     // TODO(b/261563516): Remove internal method and test aidl directly, here and elsewhere.
     @VisibleForTesting
-    MediaProjection createProjectionInternal(int uid, String packageName, int type,
-            boolean isPermanentGrant, UserHandle callingUser) {
+    MediaProjection createProjectionInternal(
+            int uid,
+            String packageName,
+            int type,
+            boolean isPermanentGrant,
+            UserHandle callingUser,
+            int displayId) {
         MediaProjection projection;
         ApplicationInfo ai;
         try {
@@ -672,8 +677,14 @@
         }
         final long callingToken = Binder.clearCallingIdentity();
         try {
-            projection = new MediaProjection(type, uid, packageName, ai.targetSdkVersion,
-                    ai.isPrivilegedApp());
+            projection =
+                    new MediaProjection(
+                            type,
+                            uid,
+                            packageName,
+                            ai.targetSdkVersion,
+                            ai.isPrivilegedApp(),
+                            displayId);
             if (isPermanentGrant) {
                 mAppOps.setMode(AppOpsManager.OP_PROJECT_MEDIA,
                         projection.uid, projection.packageName, AppOpsManager.MODE_ALLOWED);
@@ -773,11 +784,16 @@
             return hasPermission;
         }
 
-        @Override // Binder call
-        public IMediaProjection createProjection(int processUid, String packageName, int type,
-                boolean isPermanentGrant) {
+        // Binder call
+        @Override
+        public IMediaProjection createProjection(
+                int processUid,
+                String packageName,
+                int type,
+                boolean isPermanentGrant,
+                int displayId) {
             if (mContext.checkCallingPermission(MANAGE_MEDIA_PROJECTION)
-                        != PackageManager.PERMISSION_GRANTED) {
+                    != PackageManager.PERMISSION_GRANTED) {
                 throw new SecurityException("Requires MANAGE_MEDIA_PROJECTION in order to grant "
                         + "projection permission");
             }
@@ -785,8 +801,8 @@
                 throw new IllegalArgumentException("package name must not be empty");
             }
             final UserHandle callingUser = Binder.getCallingUserHandle();
-            return createProjectionInternal(processUid, packageName, type, isPermanentGrant,
-                    callingUser);
+            return createProjectionInternal(
+                    processUid, packageName, type, isPermanentGrant, callingUser, displayId);
         }
 
         @Override // Binder call
@@ -1074,6 +1090,10 @@
         private final int mTargetSdkVersion;
         private final boolean mIsPrivileged;
         private final int mType;
+        // Values for tracking token validity.
+        // Timeout value to compare creation time against.
+        private final long mTimeoutMs = mDefaultTimeoutMs;
+        private final int mDisplayId;
 
         private IMediaProjectionCallback mCallback;
         private IBinder mToken;
@@ -1082,9 +1102,6 @@
         private int mTaskId = -1;
         private LaunchCookie mLaunchCookie = null;
 
-        // Values for tracking token validity.
-        // Timeout value to compare creation time against.
-        private long mTimeoutMs = mDefaultTimeoutMs;
         // Count of number of times IMediaProjection#start is invoked.
         private int mCountStarts = 0;
         // Set if MediaProjection#createVirtualDisplay has been invoked previously (it
@@ -1093,8 +1110,13 @@
         // The associated session details already sent to WindowManager.
         private ContentRecordingSession mSession;
 
-        MediaProjection(int type, int uid, String packageName, int targetSdkVersion,
-                boolean isPrivileged) {
+        MediaProjection(
+                int type,
+                int uid,
+                String packageName,
+                int targetSdkVersion,
+                boolean isPrivileged,
+                int displayId) {
             mType = type;
             this.uid = uid;
             this.packageName = packageName;
@@ -1104,6 +1126,7 @@
             mCreateTimeMs = mClock.uptimeMillis();
             mActivityManagerInternal.notifyMediaProjectionEvent(uid, asBinder(),
                     MEDIA_PROJECTION_TOKEN_EVENT_CREATED);
+            mDisplayId = displayId;
         }
 
         int getVirtualDisplayId() {
@@ -1319,6 +1342,11 @@
             return mTaskId;
         }
 
+        @Override // Binder call
+        public int getDisplayId() {
+            return mDisplayId;
+        }
+
         @android.annotation.EnforcePermission(android.Manifest.permission.MANAGE_MEDIA_PROJECTION)
         @Override
         public boolean isValid() {
diff --git a/services/core/java/com/android/server/media/quality/MediaQualityService.java b/services/core/java/com/android/server/media/quality/MediaQualityService.java
new file mode 100644
index 0000000..40a27e8
--- /dev/null
+++ b/services/core/java/com/android/server/media/quality/MediaQualityService.java
@@ -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.server.media.quality;
+
+import android.content.Context;
+import android.media.quality.IMediaQualityManager;
+
+import com.android.server.SystemService;
+
+/**
+ * This service manage picture profile and sound profile for TV setting. Also communicates with the
+ * database to save, update the profiles.
+ */
+public class MediaQualityService extends SystemService {
+
+    private static final boolean DEBUG = false;
+    private static final String TAG = "MediaQualityService";
+    private final Context mContext;
+
+    public MediaQualityService(Context context) {
+        super(context);
+        mContext = context;
+    }
+
+    @Override
+    public void onStart() {
+        publishBinderService(Context.MEDIA_QUALITY_SERVICE, new BinderService());
+    }
+
+    // TODO: Add additional APIs. b/373951081
+    private final class BinderService extends IMediaQualityManager.Stub {
+    }
+}
diff --git a/services/core/java/com/android/server/media/quality/OWNERS b/services/core/java/com/android/server/media/quality/OWNERS
new file mode 100644
index 0000000..e455846
--- /dev/null
+++ b/services/core/java/com/android/server/media/quality/OWNERS
@@ -0,0 +1,2 @@
+shubang@google.com
+haofanw@google.com
\ No newline at end of file
diff --git a/services/core/java/com/android/server/notification/NotificationAttentionHelper.java b/services/core/java/com/android/server/notification/NotificationAttentionHelper.java
index 25741bc..0e390b6 100644
--- a/services/core/java/com/android/server/notification/NotificationAttentionHelper.java
+++ b/services/core/java/com/android/server/notification/NotificationAttentionHelper.java
@@ -1690,6 +1690,9 @@
         @Override
         public void onReceive(Context context, Intent intent) {
             String action = intent.getAction();
+            if (action == null) {
+                return;
+            }
 
             if (action.equals(Intent.ACTION_SCREEN_ON)) {
                 // Keep track of screen on/off state, but do not turn off the notification light
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 48cc032..bb0aaaf 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -49,10 +49,6 @@
 import static android.app.Notification.FLAG_PROMOTED_ONGOING;
 import static android.app.Notification.FLAG_USER_INITIATED_JOB;
 import static android.app.NotificationChannel.CONVERSATION_CHANNEL_ID_FORMAT;
-import static android.app.NotificationChannel.NEWS_ID;
-import static android.app.NotificationChannel.PROMOTIONS_ID;
-import static android.app.NotificationChannel.RECS_ID;
-import static android.app.NotificationChannel.SOCIAL_MEDIA_ID;
 import static android.app.NotificationChannel.SYSTEM_RESERVED_IDS;
 import static android.app.NotificationManager.ACTION_APP_BLOCK_STATE_CHANGED;
 import static android.app.NotificationManager.ACTION_AUTOMATIC_ZEN_RULE_STATUS_CHANGED;
@@ -108,9 +104,7 @@
 import static android.os.UserHandle.USER_SYSTEM;
 import static android.service.notification.Adjustment.KEY_TYPE;
 import static android.service.notification.Adjustment.TYPE_CONTENT_RECOMMENDATION;
-import static android.service.notification.Adjustment.TYPE_NEWS;
 import static android.service.notification.Adjustment.TYPE_PROMOTION;
-import static android.service.notification.Adjustment.TYPE_SOCIAL_MEDIA;
 import static android.service.notification.Flags.callstyleCallbackApi;
 import static android.service.notification.Flags.notificationClassification;
 import static android.service.notification.Flags.notificationForceGrouping;
@@ -5689,14 +5683,16 @@
 
                 final int zenMode = zenModeFromInterruptionFilter(interruptionFilter, -1);
                 if (zenMode == -1) return;
+
+                UserHandle zenUser = getCallingZenUser();
                 if (!canManageGlobalZenPolicy(info.component.getPackageName(), callingUid)) {
                     mZenModeHelper.applyGlobalZenModeAsImplicitZenRule(
-                            info.component.getPackageName(), callingUid, zenMode);
+                            zenUser, info.component.getPackageName(), callingUid, zenMode);
                 } else {
                     int origin = computeZenOrigin(/* fromUser= */ false);
                     Binder.withCleanCallingIdentity(() -> {
-                        mZenModeHelper.setManualZenMode(zenMode, /* conditionId= */ null, origin,
-                                "listener:" + info.component.flattenToShortString(),
+                        mZenModeHelper.setManualZenMode(zenUser, zenMode, /* conditionId= */ null,
+                                origin, "listener:" + info.component.flattenToShortString(),
                                 /* caller= */ info.component.getPackageName(),
                                 callingUid);
                     });
@@ -5751,12 +5747,13 @@
         public void setZenMode(int mode, Uri conditionId, String reason, boolean fromUser) {
             enforceSystemOrSystemUI("INotificationManager.setZenMode");
             enforceUserOriginOnlyFromSystem(fromUser, "setZenMode");
+            UserHandle zenUser = getCallingZenUser();
 
             final int callingUid = Binder.getCallingUid();
             final long identity = Binder.clearCallingIdentity();
             try {
-                mZenModeHelper.setManualZenMode(mode, conditionId, computeZenOrigin(fromUser),
-                        reason, /* caller= */ null, callingUid);
+                mZenModeHelper.setManualZenMode(zenUser, mode, conditionId,
+                        computeZenOrigin(fromUser), reason, /* caller= */ null, callingUid);
             } finally {
                 Binder.restoreCallingIdentity(identity);
             }
@@ -5766,7 +5763,7 @@
         @Override
         public List<ZenModeConfig.ZenRule> getZenRules() throws RemoteException {
             enforcePolicyAccess(Binder.getCallingUid(), "getZenRules");
-            return mZenModeHelper.getZenRules();
+            return mZenModeHelper.getZenRules(getCallingZenUser());
         }
 
         @Override
@@ -5775,14 +5772,14 @@
                 throw new IllegalStateException("getAutomaticZenRules called with flag off!");
             }
             enforcePolicyAccess(Binder.getCallingUid(), "getAutomaticZenRules");
-            return mZenModeHelper.getAutomaticZenRules();
+            return mZenModeHelper.getAutomaticZenRules(getCallingZenUser());
         }
 
         @Override
         public AutomaticZenRule getAutomaticZenRule(String id) throws RemoteException {
             Objects.requireNonNull(id, "Id is null");
             enforcePolicyAccess(Binder.getCallingUid(), "getAutomaticZenRule");
-            return mZenModeHelper.getAutomaticZenRule(id);
+            return mZenModeHelper.getAutomaticZenRule(getCallingZenUser(), id);
         }
 
         @Override
@@ -5797,6 +5794,7 @@
             }
             enforcePolicyAccess(Binder.getCallingUid(), "addAutomaticZenRule");
             enforceUserOriginOnlyFromSystem(fromUser, "addAutomaticZenRule");
+            UserHandle zenUser = getCallingZenUser();
 
             // If the calling app is the system (from any user), take the package name from the
             // rule's owner rather than from the caller's package.
@@ -5807,16 +5805,18 @@
                 }
             }
 
-            return mZenModeHelper.addAutomaticZenRule(rulePkg, automaticZenRule,
+            return mZenModeHelper.addAutomaticZenRule(zenUser, rulePkg, automaticZenRule,
                     computeZenOrigin(fromUser), "addAutomaticZenRule", Binder.getCallingUid());
         }
 
         @Override
         public void setManualZenRuleDeviceEffects(ZenDeviceEffects effects) throws RemoteException {
             checkCallerIsSystem();
+            UserHandle zenUser = getCallingZenUser();
 
-            mZenModeHelper.setManualZenRuleDeviceEffects(effects, computeZenOrigin(true),
-                    "Update manual mode non-policy settings", Binder.getCallingUid());
+            mZenModeHelper.setManualZenRuleDeviceEffects(zenUser, effects,
+                    computeZenOrigin(true), "Update manual mode non-policy settings",
+                    Binder.getCallingUid());
         }
 
         @Override
@@ -5825,8 +5825,9 @@
             validateAutomaticZenRule(id, automaticZenRule);
             enforcePolicyAccess(Binder.getCallingUid(), "updateAutomaticZenRule");
             enforceUserOriginOnlyFromSystem(fromUser, "updateAutomaticZenRule");
+            UserHandle zenUser = getCallingZenUser();
 
-            return mZenModeHelper.updateAutomaticZenRule(id, automaticZenRule,
+            return mZenModeHelper.updateAutomaticZenRule(zenUser, id, automaticZenRule,
                     computeZenOrigin(fromUser), "updateAutomaticZenRule", Binder.getCallingUid());
         }
 
@@ -5892,8 +5893,9 @@
             // Verify that they can modify zen rules.
             enforcePolicyAccess(Binder.getCallingUid(), "removeAutomaticZenRule");
             enforceUserOriginOnlyFromSystem(fromUser, "removeAutomaticZenRule");
+            UserHandle zenUser = getCallingZenUser();
 
-            return mZenModeHelper.removeAutomaticZenRule(id, computeZenOrigin(fromUser),
+            return mZenModeHelper.removeAutomaticZenRule(zenUser, id, computeZenOrigin(fromUser),
                     "removeAutomaticZenRule", Binder.getCallingUid());
         }
 
@@ -5903,9 +5905,11 @@
             Objects.requireNonNull(packageName, "Package name is null");
             enforceSystemOrSystemUI("removeAutomaticZenRules");
             enforceUserOriginOnlyFromSystem(fromUser, "removeAutomaticZenRules");
+            UserHandle zenUser = getCallingZenUser();
 
-            return mZenModeHelper.removeAutomaticZenRules(packageName, computeZenOrigin(fromUser),
-                    packageName + "|removeAutomaticZenRules", Binder.getCallingUid());
+            return mZenModeHelper.removeAutomaticZenRules(zenUser, packageName,
+                    computeZenOrigin(fromUser), packageName + "|removeAutomaticZenRules",
+                    Binder.getCallingUid());
         }
 
         @Override
@@ -5913,7 +5917,7 @@
             Objects.requireNonNull(owner, "Owner is null");
             enforceSystemOrSystemUI("getRuleInstanceCount");
 
-            return mZenModeHelper.getCurrentInstanceCount(owner);
+            return mZenModeHelper.getCurrentInstanceCount(getCallingZenUser(), owner);
         }
 
         @Override
@@ -5921,7 +5925,7 @@
         public int getAutomaticZenRuleState(@NonNull String id) {
             Objects.requireNonNull(id, "id is null");
             enforcePolicyAccess(Binder.getCallingUid(), "getAutomaticZenRuleState");
-            return mZenModeHelper.getAutomaticZenRuleState(id);
+            return mZenModeHelper.getAutomaticZenRuleState(getCallingZenUser(), id);
         }
 
         @Override
@@ -5932,9 +5936,30 @@
 
             enforcePolicyAccess(Binder.getCallingUid(), "setAutomaticZenRuleState");
             boolean fromUser = (condition.source == Condition.SOURCE_USER_ACTION);
+            UserHandle zenUser = getCallingZenUser();
 
-            mZenModeHelper.setAutomaticZenRuleState(id, condition, computeZenOrigin(fromUser),
-                    Binder.getCallingUid());
+            mZenModeHelper.setAutomaticZenRuleState(zenUser, id, condition,
+                    computeZenOrigin(fromUser), Binder.getCallingUid());
+        }
+
+        /**
+         * Returns the {@link UserHandle} corresponding to the caller that is performing a
+         * zen-related operation (such as {@link #setInterruptionFilter},
+         * {@link #addAutomaticZenRule}, {@link #setAutomaticZenRuleState}, etc). The user is
+         * {@link UserHandle#USER_CURRENT} if the caller is the system or SystemUI (assuming
+         * that all interactions in SystemUI are for the "current" user); otherwise it's the user
+         * associated to the binder call.
+         */
+        private UserHandle getCallingZenUser() {
+            if (android.app.Flags.modesMultiuser()) {
+                if (isCallerSystemOrSystemUiOrShell()) {
+                    return UserHandle.CURRENT;
+                } else {
+                    return Binder.getCallingUserHandle();
+                }
+            } else {
+                return UserHandle.CURRENT;
+            }
         }
 
         @ZenModeConfig.ConfigOrigin
@@ -5970,15 +5995,16 @@
             if (zen == -1) throw new IllegalArgumentException("Invalid filter: " + filter);
             final int callingUid = Binder.getCallingUid();
             enforceUserOriginOnlyFromSystem(fromUser, "setInterruptionFilter");
+            UserHandle zenUser = getCallingZenUser();
 
             if (android.app.Flags.modesApi() && !canManageGlobalZenPolicy(pkg, callingUid)) {
-                mZenModeHelper.applyGlobalZenModeAsImplicitZenRule(pkg, callingUid, zen);
+                mZenModeHelper.applyGlobalZenModeAsImplicitZenRule(zenUser, pkg, callingUid, zen);
                 return;
             }
 
             final long identity = Binder.clearCallingIdentity();
             try {
-                mZenModeHelper.setManualZenMode(zen, null, computeZenOrigin(fromUser),
+                mZenModeHelper.setManualZenMode(zenUser, zen, null, computeZenOrigin(fromUser),
                         /* reason= */ "setInterruptionFilter", /* caller= */ pkg,
                         callingUid);
             } finally {
@@ -6274,12 +6300,13 @@
         @Override
         public Policy getNotificationPolicy(String pkg) {
             final int callingUid = Binder.getCallingUid();
+            UserHandle zenUser = getCallingZenUser();
             if (android.app.Flags.modesApi() && !canManageGlobalZenPolicy(pkg, callingUid)) {
-                return mZenModeHelper.getNotificationPolicyFromImplicitZenRule(pkg);
+                return mZenModeHelper.getNotificationPolicyFromImplicitZenRule(zenUser, pkg);
             }
             final long identity = Binder.clearCallingIdentity();
             try {
-                return mZenModeHelper.getNotificationPolicy();
+                return mZenModeHelper.getNotificationPolicy(zenUser);
             } finally {
                 Binder.restoreCallingIdentity(identity);
             }
@@ -6308,6 +6335,7 @@
             enforceUserOriginOnlyFromSystem(fromUser, "setNotificationPolicy");
             int callingUid = Binder.getCallingUid();
             @ZenModeConfig.ConfigOrigin int origin = computeZenOrigin(fromUser);
+            UserHandle zenUser = getCallingZenUser();
 
             boolean isSystemCaller = isCallerSystemOrSystemUiOrShell();
             boolean shouldApplyAsImplicitRule = android.app.Flags.modesApi()
@@ -6317,7 +6345,7 @@
             try {
                 final ApplicationInfo applicationInfo = mPackageManager.getApplicationInfo(pkg,
                         0, UserHandle.getUserId(callingUid));
-                Policy currPolicy = mZenModeHelper.getNotificationPolicy();
+                Policy currPolicy = mZenModeHelper.getNotificationPolicy(zenUser);
 
                 if (applicationInfo.targetSdkVersion < Build.VERSION_CODES.P) {
                     int priorityCategories = policy.priorityCategories;
@@ -6375,11 +6403,12 @@
                 }
 
                 if (shouldApplyAsImplicitRule) {
-                    mZenModeHelper.applyGlobalPolicyAsImplicitZenRule(pkg, callingUid, policy);
+                    mZenModeHelper.applyGlobalPolicyAsImplicitZenRule(zenUser, pkg, callingUid,
+                            policy);
                 } else {
                     ZenLog.traceSetNotificationPolicy(pkg, applicationInfo.targetSdkVersion,
                             policy);
-                    mZenModeHelper.setNotificationPolicy(policy, origin, callingUid);
+                    mZenModeHelper.setNotificationPolicy(zenUser, policy, origin, callingUid);
                 }
             } catch (RemoteException e) {
                 Slog.e(TAG, "Failed to set notification policy", e);
@@ -6924,21 +6953,19 @@
 
     @GuardedBy("mNotificationLock")
     @Nullable
+    @FlaggedApi(android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION)
     private NotificationChannel getClassificationChannelLocked(NotificationRecord r,
             Bundle adjustments) {
         int type = adjustments.getInt(KEY_TYPE);
-        if (TYPE_NEWS == type) {
-            return mPreferencesHelper.getNotificationChannel(
-                    r.getSbn().getPackageName(), r.getUid(), NEWS_ID, false);
-        } else if (TYPE_PROMOTION == type) {
-            return mPreferencesHelper.getNotificationChannel(
-                    r.getSbn().getPackageName(), r.getUid(), PROMOTIONS_ID, false);
-        } else if (TYPE_SOCIAL_MEDIA == type) {
-            return mPreferencesHelper.getNotificationChannel(
-                    r.getSbn().getPackageName(), r.getUid(), SOCIAL_MEDIA_ID, false);
-        } else if (TYPE_CONTENT_RECOMMENDATION == type) {
-            return mPreferencesHelper.getNotificationChannel(
-                    r.getSbn().getPackageName(), r.getUid(), RECS_ID, false);
+        if (type >= TYPE_PROMOTION && type <= TYPE_CONTENT_RECOMMENDATION) {
+            NotificationChannel channel = mPreferencesHelper.getReservedChannel(
+                    r.getSbn().getPackageName(), r.getUid(), type);
+            if (channel == null) {
+                channel = mPreferencesHelper.createReservedChannel(
+                        r.getSbn().getPackageName(), r.getUid(), type);
+                handleSavePolicyFile();
+            }
+            return channel;
         }
         return null;
     }
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index 964a5d0..9f0b4b0 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -17,6 +17,7 @@
 package com.android.server.notification;
 
 import static android.app.AppOpsManager.OP_SYSTEM_ALERT_WINDOW;
+import static android.app.NotificationChannel.DEFAULT_CHANNEL_ID;
 import static android.app.NotificationChannel.NEWS_ID;
 import static android.app.NotificationChannel.PLACEHOLDER_CONVERSATION_ID;
 import static android.app.NotificationChannel.PROMOTIONS_ID;
@@ -32,6 +33,10 @@
 import static android.app.NotificationManager.IMPORTANCE_NONE;
 import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
 import static android.os.UserHandle.USER_SYSTEM;
+import static android.service.notification.Adjustment.TYPE_CONTENT_RECOMMENDATION;
+import static android.service.notification.Adjustment.TYPE_NEWS;
+import static android.service.notification.Adjustment.TYPE_PROMOTION;
+import static android.service.notification.Adjustment.TYPE_SOCIAL_MEDIA;
 import static android.service.notification.Flags.notificationClassification;
 
 import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_CHANNEL_GROUP_PREFERENCES;
@@ -66,6 +71,7 @@
 import android.os.UserHandle;
 import android.permission.PermissionManager;
 import android.provider.Settings;
+import android.service.notification.Adjustment;
 import android.service.notification.ConversationChannelWrapper;
 import android.service.notification.NotificationListenerService;
 import android.service.notification.RankingHelperProto;
@@ -549,10 +555,6 @@
                 Slog.e(TAG, "createDefaultChannelIfNeededLocked - Exception: " + e);
             }
 
-            if (notificationClassification()) {
-                addReservedChannelsLocked(r);
-            }
-
             if (r.uid == UNKNOWN_UID) {
                 if (Flags.persistIncompleteRestoreData()) {
                     r.userId = userId;
@@ -587,7 +589,7 @@
 
     private boolean deleteDefaultChannelIfNeededLocked(PackagePreferences r) throws
             PackageManager.NameNotFoundException {
-        if (!r.channels.containsKey(NotificationChannel.DEFAULT_CHANNEL_ID)) {
+        if (!r.channels.containsKey(DEFAULT_CHANNEL_ID)) {
             // Not present
             return false;
         }
@@ -598,7 +600,7 @@
         }
 
         // Remove Default Channel.
-        r.channels.remove(NotificationChannel.DEFAULT_CHANNEL_ID);
+        r.channels.remove(DEFAULT_CHANNEL_ID);
 
         return true;
     }
@@ -609,8 +611,8 @@
             return false;
         }
 
-        if (r.channels.containsKey(NotificationChannel.DEFAULT_CHANNEL_ID)) {
-            r.channels.get(NotificationChannel.DEFAULT_CHANNEL_ID).setName(mContext.getString(
+        if (r.channels.containsKey(DEFAULT_CHANNEL_ID)) {
+            r.channels.get(DEFAULT_CHANNEL_ID).setName(mContext.getString(
                     com.android.internal.R.string.default_notification_channel_label));
             return false;
         }
@@ -623,7 +625,7 @@
         // Create Default Channel
         NotificationChannel channel;
         channel = new NotificationChannel(
-                NotificationChannel.DEFAULT_CHANNEL_ID,
+                DEFAULT_CHANNEL_ID,
                 mContext.getString(R.string.default_notification_channel_label),
                 r.importance);
         channel.setBypassDnd(r.priority == Notification.PRIORITY_MAX);
@@ -642,38 +644,25 @@
         return true;
     }
 
-    private void addReservedChannelsLocked(PackagePreferences p) {
-        if (!p.channels.containsKey(NotificationChannel.PROMOTIONS_ID)) {
-            NotificationChannel channel = new NotificationChannel(
-                    NotificationChannel.PROMOTIONS_ID,
-                    mContext.getString(R.string.promotional_notification_channel_label),
-                    IMPORTANCE_LOW);
-            p.channels.put(channel.getId(), channel);
+    private NotificationChannel addReservedChannelLocked(PackagePreferences p, String channelId) {
+        String label = "";
+        switch (channelId) {
+            case PROMOTIONS_ID:
+                label = mContext.getString(R.string.promotional_notification_channel_label);
+                break;
+            case RECS_ID:
+                label = mContext.getString(R.string.recs_notification_channel_label);
+                break;
+            case NEWS_ID:
+                label = mContext.getString(R.string.news_notification_channel_label);
+                break;
+            case SOCIAL_MEDIA_ID:
+                label = mContext.getString(R.string.social_notification_channel_label);
+                break;
         }
-
-        if (!p.channels.containsKey(NotificationChannel.RECS_ID)) {
-            NotificationChannel channel = new NotificationChannel(
-                    NotificationChannel.RECS_ID,
-                    mContext.getString(R.string.recs_notification_channel_label),
-                    IMPORTANCE_LOW);
-            p.channels.put(channel.getId(), channel);
-        }
-
-        if (!p.channels.containsKey(NotificationChannel.NEWS_ID)) {
-            NotificationChannel channel = new NotificationChannel(
-                    NotificationChannel.NEWS_ID,
-                    mContext.getString(R.string.news_notification_channel_label),
-                    IMPORTANCE_LOW);
-            p.channels.put(channel.getId(), channel);
-        }
-
-        if (!p.channels.containsKey(NotificationChannel.SOCIAL_MEDIA_ID)) {
-            NotificationChannel channel = new NotificationChannel(
-                    NotificationChannel.SOCIAL_MEDIA_ID,
-                    mContext.getString(R.string.social_notification_channel_label),
-                    IMPORTANCE_LOW);
-            p.channels.put(channel.getId(), channel);
-        }
+        NotificationChannel channel = new NotificationChannel(channelId, label, IMPORTANCE_LOW);
+        p.channels.put(channelId, channel);
+        return channel;
     }
 
     public void writeXml(TypedXmlSerializer out, boolean forBackup, int userId) throws IOException {
@@ -1078,7 +1067,7 @@
             if (channel.getGroup() != null && !r.groups.containsKey(channel.getGroup())) {
                 throw new IllegalArgumentException("NotificationChannelGroup doesn't exist");
             }
-            if (NotificationChannel.DEFAULT_CHANNEL_ID.equals(channel.getId())) {
+            if (DEFAULT_CHANNEL_ID.equals(channel.getId())) {
                 throw new IllegalArgumentException("Reserved id");
             }
             // Only the user can update bundle channel settings
@@ -1411,6 +1400,54 @@
         }
     }
 
+    private @Nullable String getChannelIdForBundleType(@Adjustment.Types int type) {
+        switch (type) {
+            case TYPE_CONTENT_RECOMMENDATION:
+                return RECS_ID;
+            case TYPE_NEWS:
+                return NEWS_ID;
+            case TYPE_PROMOTION:
+                return PROMOTIONS_ID;
+            case TYPE_SOCIAL_MEDIA:
+                return SOCIAL_MEDIA_ID;
+        }
+        return null;
+    }
+
+    @FlaggedApi(android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION)
+    public NotificationChannel getReservedChannel(String pkg, int uid,
+            @Adjustment.Types int type) {
+        if (!notificationClassification()) {
+            return null;
+        }
+        Objects.requireNonNull(pkg);
+        String channelId = getChannelIdForBundleType(type);
+        if (channelId == null) {
+            return null;
+        }
+        NotificationChannel channel =
+                getConversationNotificationChannel(pkg, uid, channelId, null, true, false);
+        return channel;
+    }
+
+    @FlaggedApi(android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION)
+    public NotificationChannel createReservedChannel(String pkg, int uid,
+            @Adjustment.Types int type) {
+        if (!notificationClassification()) {
+            return null;
+        }
+        Objects.requireNonNull(pkg);
+        PackagePreferences r = getOrCreatePackagePreferencesLocked(pkg, uid);
+        if (r == null) {
+            return null;
+        }
+        String channelId = getChannelIdForBundleType(type);
+        if (channelId == null) {
+            return null;
+        }
+        return addReservedChannelLocked(r, channelId);
+    }
+
     @Override
     public NotificationChannel getNotificationChannel(String pkg, int uid, String channelId,
             boolean includeDeleted) {
@@ -1429,7 +1466,7 @@
                 return null;
             }
             if (channelId == null) {
-                channelId = NotificationChannel.DEFAULT_CHANNEL_ID;
+                channelId = DEFAULT_CHANNEL_ID;
             }
             NotificationChannel channel = null;
             if (conversationId != null) {
@@ -1540,7 +1577,7 @@
             int N = r.channels.size() - 1;
             for (int i = N; i >= 0; i--) {
                 String key = r.channels.keyAt(i);
-                if (!NotificationChannel.DEFAULT_CHANNEL_ID.equals(key)) {
+                if (!DEFAULT_CHANNEL_ID.equals(key)) {
                     r.channels.remove(key);
                 }
             }
@@ -1658,10 +1695,7 @@
                         && (activeChannelFilter == null
                                 || (includeBlocked && nc.getImportance() == IMPORTANCE_NONE)
                                 || activeChannelFilter.contains(nc.getId()))
-                        && !PROMOTIONS_ID.equals(nc.getId())
-                        && !NEWS_ID.equals(nc.getId())
-                        && !SOCIAL_MEDIA_ID.equals(nc.getId())
-                        && !RECS_ID.equals(nc.getId());
+                        && !SYSTEM_RESERVED_IDS.contains(nc.getId());
                 if (includeChannel) {
                     if (nc.getGroup() != null) {
                         if (r.groups.get(nc.getGroup()) != null) {
@@ -1924,9 +1958,23 @@
     public boolean onlyHasDefaultChannel(String pkg, int uid) {
         synchronized (mLock) {
             PackagePreferences r = getOrCreatePackagePreferencesLocked(pkg, uid);
-            if (r.channels.size() == (notificationClassification() ? 5 : 1)
-                    && r.channels.containsKey(NotificationChannel.DEFAULT_CHANNEL_ID)) {
-                return true;
+            if (r.channels.containsKey(DEFAULT_CHANNEL_ID)) {
+                if (r.channels.size() == 1) {
+                    return true;
+                }
+                if (notificationClassification()) {
+                    if (r.channels.size() <= 5) {
+                        for (NotificationChannel c : r.channels.values()) {
+                            if (!SYSTEM_RESERVED_IDS.contains(c.getId()) &&
+                                    !DEFAULT_CHANNEL_ID.equals(c.getId())) {
+                                return false;
+                            }
+                            return true;
+                        }
+                    } else {
+                        return false;
+                    }
+                }
             }
             return false;
         }
@@ -1980,8 +2028,9 @@
      * </ul>
      */
     void syncChannelsBypassingDnd() {
-        mCurrentUserHasChannelsBypassingDnd = (mZenModeHelper.getNotificationPolicy().state
-                & NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND) != 0;
+        mCurrentUserHasChannelsBypassingDnd =
+                (mZenModeHelper.getNotificationPolicy(UserHandle.CURRENT).state
+                        & NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND) != 0;
 
         updateCurrentUserHasChannelsBypassingDnd(/* callingUid= */ Process.SYSTEM_UID,
                 /* fromSystemOrSystemUi= */ true);
@@ -2024,7 +2073,8 @@
         if (mCurrentUserHasChannelsBypassingDnd != haveBypassingApps) {
             mCurrentUserHasChannelsBypassingDnd = haveBypassingApps;
             if (android.app.Flags.modesUi()) {
-                mZenModeHelper.updateHasPriorityChannels(mCurrentUserHasChannelsBypassingDnd);
+                mZenModeHelper.updateHasPriorityChannels(UserHandle.CURRENT,
+                        mCurrentUserHasChannelsBypassingDnd);
             } else {
                 updateZenPolicy(mCurrentUserHasChannelsBypassingDnd, callingUid,
                         fromSystemOrSystemUi);
@@ -2051,8 +2101,10 @@
     //                     PreferencesHelper should otherwise not need to modify actual policy
     public void updateZenPolicy(boolean areChannelsBypassingDnd, int callingUid,
             boolean fromSystemOrSystemUi) {
-        NotificationManager.Policy policy = mZenModeHelper.getNotificationPolicy();
+        NotificationManager.Policy policy = mZenModeHelper.getNotificationPolicy(
+                UserHandle.CURRENT);
         mZenModeHelper.setNotificationPolicy(
+                UserHandle.CURRENT,
                 new NotificationManager.Policy(
                         policy.priorityCategories, policy.priorityCallSenders,
                         policy.priorityMessageSenders, policy.suppressedVisualEffects,
@@ -2744,9 +2796,9 @@
                 PackagePreferences PackagePreferences = mPackagePreferences.valueAt(i);
                 if (UserHandle.getUserId(PackagePreferences.uid) == userId) {
                     if (PackagePreferences.channels.containsKey(
-                            NotificationChannel.DEFAULT_CHANNEL_ID)) {
+                            DEFAULT_CHANNEL_ID)) {
                         PackagePreferences.channels.get(
-                                NotificationChannel.DEFAULT_CHANNEL_ID).setName(
+                                DEFAULT_CHANNEL_ID).setName(
                                 context.getResources().getString(
                                         R.string.default_notification_channel_label));
                     }
diff --git a/services/core/java/com/android/server/notification/TimeToLiveHelper.java b/services/core/java/com/android/server/notification/TimeToLiveHelper.java
index cabe766..b053dfe 100644
--- a/services/core/java/com/android/server/notification/TimeToLiveHelper.java
+++ b/services/core/java/com/android/server/notification/TimeToLiveHelper.java
@@ -188,7 +188,11 @@
                         timeoutKey = earliest.second;
                     }
                 }
-                mNm.timeoutNotification(timeoutKey);
+                if (timeoutKey != null) {
+                    mNm.timeoutNotification(timeoutKey);
+                } else {
+                    Slog.wtf(TAG, "Alarm triggered but should have been cleaned up already");
+                }
             }
         }
     };
diff --git a/services/core/java/com/android/server/notification/ZenModeConditions.java b/services/core/java/com/android/server/notification/ZenModeConditions.java
index b1f010c..52d0c41 100644
--- a/services/core/java/com/android/server/notification/ZenModeConditions.java
+++ b/services/core/java/com/android/server/notification/ZenModeConditions.java
@@ -20,6 +20,7 @@
 import android.net.Uri;
 import android.os.Binder;
 import android.os.Process;
+import android.os.UserHandle;
 import android.service.notification.Condition;
 import android.service.notification.IConditionProvider;
 import android.service.notification.ZenModeConfig;
@@ -117,7 +118,10 @@
         ZenModeConfig config = mHelper.getConfig();
         if (config == null) return;
         final int callingUid = Binder.getCallingUid();
-        mHelper.setAutomaticZenRuleState(id, condition,
+
+        // This change is known to be for UserHandle.CURRENT because ConditionProviders for
+        // background users are not bound.
+        mHelper.setAutomaticZenRuleState(UserHandle.CURRENT, id, condition,
                 callingUid == Process.SYSTEM_UID ? ZenModeConfig.ORIGIN_SYSTEM
                         : ZenModeConfig.ORIGIN_APP,
                 callingUid);
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index 5547bd3..3900870 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -174,8 +174,7 @@
     private final RingerModeDelegate mRingerModeDelegate = new
             RingerModeDelegate();
     @VisibleForTesting protected final ZenModeConditions mConditions;
-    private final Object mConfigsArrayLock = new Object();
-    @GuardedBy("mConfigsArrayLock")
+    @GuardedBy("mConfigLock")
     @VisibleForTesting final SparseArray<ZenModeConfig> mConfigs = new SparseArray<>();
     private final Metrics mMetrics = new Metrics();
     private final ConditionProviders.Config mServiceConfig;
@@ -222,8 +221,8 @@
                 : readDefaultConfig(mContext.getResources());
         updateDefaultConfig(mContext, mDefaultConfig);
 
-        mConfig = mDefaultConfig.copy();
-        synchronized (mConfigsArrayLock) {
+        synchronized (mConfigLock) {
+            mConfig = mDefaultConfig.copy();
             mConfigs.put(UserHandle.USER_SYSTEM, mConfig);
         }
         mConsolidatedPolicy = mConfig.toNotificationPolicy();
@@ -237,10 +236,6 @@
         mZenModeEventLogger = zenModeEventLogger;
     }
 
-    public Looper getLooper() {
-        return mHandler.getLooper();
-    }
-
     @Override
     public String toString() {
         return TAG;
@@ -331,7 +326,7 @@
     public void onUserRemoved(int user) {
         if (user < UserHandle.USER_SYSTEM) return;
         if (DEBUG) Log.d(TAG, "onUserRemoved u=" + user);
-        synchronized (mConfigsArrayLock) {
+        synchronized (mConfigLock) {
             mConfigs.remove(user);
         }
     }
@@ -350,7 +345,7 @@
         mUser = user;
         if (DEBUG) Log.d(TAG, reason + " u=" + user);
         ZenModeConfig config = null;
-        synchronized (mConfigsArrayLock) {
+        synchronized (mConfigLock) {
             if (mConfigs.get(user) != null) {
                 config = mConfigs.get(user).copy();
             }
@@ -376,7 +371,9 @@
             boolean fromSystemOrSystemUi) {
         final int newZen = NotificationManager.zenModeFromInterruptionFilter(filter, -1);
         if (newZen != -1) {
-            setManualZenMode(newZen, null,
+            // This change is known to be for UserHandle.CURRENT because NLSes for
+            // background users are unbound.
+            setManualZenMode(UserHandle.CURRENT, newZen, null,
                     fromSystemOrSystemUi ? ORIGIN_SYSTEM : ORIGIN_APP,
                     /* reason= */ "listener:" + (name != null ? name.flattenToShortString() : null),
                     /* caller= */ name != null ? name.getPackageName() : null,
@@ -399,11 +396,12 @@
     }
 
     // TODO: b/310620812 - Make private (or inline) when MODES_API is inlined.
-    public List<ZenRule> getZenRules() {
+    public List<ZenRule> getZenRules(UserHandle user) {
         List<ZenRule> rules = new ArrayList<>();
         synchronized (mConfigLock) {
-            if (mConfig == null) return rules;
-            for (ZenRule rule : mConfig.automaticRules.values()) {
+            ZenModeConfig config = getConfigLocked(user);
+            if (config == null) return rules;
+            for (ZenRule rule : config.automaticRules.values()) {
                 if (canManageAutomaticZenRule(rule)) {
                     rules.add(rule);
                 }
@@ -417,8 +415,8 @@
      * (which means the owned rules for a regular app, and every rule for system callers) together
      * with their ids.
      */
-    Map<String, AutomaticZenRule> getAutomaticZenRules() {
-        List<ZenRule> ruleList = getZenRules();
+    Map<String, AutomaticZenRule> getAutomaticZenRules(UserHandle user) {
+        List<ZenRule> ruleList = getZenRules(user);
         HashMap<String, AutomaticZenRule> rules = new HashMap<>(ruleList.size());
         for (ZenRule rule : ruleList) {
             rules.put(rule.id, zenRuleToAutomaticZenRule(rule));
@@ -426,11 +424,12 @@
         return rules;
     }
 
-    public AutomaticZenRule getAutomaticZenRule(String id) {
+    public AutomaticZenRule getAutomaticZenRule(UserHandle user, String id) {
         ZenRule rule;
         synchronized (mConfigLock) {
-            if (mConfig == null) return null;
-            rule = mConfig.automaticRules.get(id);
+            ZenModeConfig config = getConfigLocked(user);
+            if (config == null) return null;
+            rule = config.automaticRules.get(id);
         }
         if (rule == null) return null;
         if (canManageAutomaticZenRule(rule)) {
@@ -439,8 +438,9 @@
         return null;
     }
 
-    public String addAutomaticZenRule(String pkg, AutomaticZenRule automaticZenRule,
-            @ConfigOrigin int origin, String reason, int callingUid) {
+    public String addAutomaticZenRule(UserHandle user, String pkg,
+            AutomaticZenRule automaticZenRule, @ConfigOrigin int origin, String reason,
+            int callingUid) {
         checkManageRuleOrigin("addAutomaticZenRule", origin);
         if (!ZenModeConfig.SYSTEM_AUTHORITY.equals(pkg)) {
             PackageItemInfo component = getServiceInfo(automaticZenRule.getOwner());
@@ -455,10 +455,10 @@
                 ruleInstanceLimit = component.metaData.getInt(
                         ConditionProviderService.META_DATA_RULE_INSTANCE_LIMIT, -1);
             }
-            int newRuleInstanceCount = getCurrentInstanceCount(automaticZenRule.getOwner())
-                    + getCurrentInstanceCount(automaticZenRule.getConfigurationActivity())
+            int newRuleInstanceCount = getCurrentInstanceCount(user, automaticZenRule.getOwner())
+                    + getCurrentInstanceCount(user, automaticZenRule.getConfigurationActivity())
                     + 1;
-            int newPackageRuleCount = getPackageRuleCount(pkg) + 1;
+            int newPackageRuleCount = getPackageRuleCount(user, pkg) + 1;
             if (newPackageRuleCount > RULE_LIMIT_PER_PACKAGE
                     || (ruleInstanceLimit > 0 && ruleInstanceLimit < newRuleInstanceCount)) {
                 throw new IllegalArgumentException("Rule instance limit exceeded");
@@ -467,15 +467,16 @@
 
         ZenModeConfig newConfig;
         synchronized (mConfigLock) {
-            if (mConfig == null) {
+            ZenModeConfig config = getConfigLocked(user);
+            if (config == null) {
                 throw new AndroidRuntimeException("Could not create rule");
             }
             if (DEBUG) {
                 Log.d(TAG, "addAutomaticZenRule rule= " + automaticZenRule + " reason=" + reason);
             }
-            newConfig = mConfig.copy();
+            newConfig = config.copy();
             ZenRule rule = new ZenRule();
-            populateZenRule(pkg, automaticZenRule, rule, origin, /* isNew= */ true);
+            populateZenRule(pkg, automaticZenRule, newConfig, rule, origin, /* isNew= */ true);
             rule = maybeRestoreRemovedRule(newConfig, pkg, rule, automaticZenRule, origin);
             newConfig.automaticRules.put(rule.id, rule);
             maybeReplaceDefaultRule(newConfig, null, automaticZenRule);
@@ -524,7 +525,7 @@
 
         // "Preserve" the previous rule by considering the azrToAdd an update instead.
         // Only app-modifiable fields will actually be modified.
-        populateZenRule(pkg, azrToAdd, ruleToRestore, origin, /* isNew= */ false);
+        populateZenRule(pkg, azrToAdd, config, ruleToRestore, origin, /* isNew= */ false);
         return ruleToRestore;
     }
 
@@ -558,35 +559,37 @@
         }
     }
 
-    public boolean updateAutomaticZenRule(String ruleId, AutomaticZenRule automaticZenRule,
-            @ConfigOrigin int origin, String reason, int callingUid) {
+    public boolean updateAutomaticZenRule(UserHandle user, String ruleId,
+            AutomaticZenRule automaticZenRule, @ConfigOrigin int origin, String reason,
+            int callingUid) {
         checkManageRuleOrigin("updateAutomaticZenRule", origin);
         if (ruleId == null) {
             throw new IllegalArgumentException("ruleId cannot be null");
         }
         synchronized (mConfigLock) {
-            if (mConfig == null) return false;
+            ZenModeConfig config = getConfigLocked(user);
+            if (config == null) return false;
             if (DEBUG) {
                 Log.d(TAG, "updateAutomaticZenRule zenRule=" + automaticZenRule
                         + " reason=" + reason);
             }
-            ZenModeConfig.ZenRule oldRule = mConfig.automaticRules.get(ruleId);
+            ZenModeConfig.ZenRule oldRule = config.automaticRules.get(ruleId);
             if (oldRule == null || !canManageAutomaticZenRule(oldRule)) {
                 throw new SecurityException(
                         "Cannot update rules not owned by your condition provider");
             }
-            ZenModeConfig newConfig = mConfig.copy();
+            ZenModeConfig newConfig = config.copy();
             ZenModeConfig.ZenRule newRule = requireNonNull(newConfig.automaticRules.get(ruleId));
             if (!Flags.modesApi()) {
                 if (newRule.enabled != automaticZenRule.isEnabled()) {
-                    dispatchOnAutomaticRuleStatusChanged(mConfig.user, newRule.getPkg(), ruleId,
+                    dispatchOnAutomaticRuleStatusChanged(config.user, newRule.getPkg(), ruleId,
                             automaticZenRule.isEnabled()
                                     ? AUTOMATIC_RULE_STATUS_ENABLED
                                     : AUTOMATIC_RULE_STATUS_DISABLED);
                 }
             }
 
-            boolean updated = populateZenRule(newRule.pkg, automaticZenRule, newRule,
+            boolean updated = populateZenRule(newRule.pkg, automaticZenRule, newConfig, newRule,
                     origin, /* isNew= */ false);
             if (Flags.modesApi() && !updated) {
                 // Bail out so we don't have the side effects of updating a rule (i.e. dropping
@@ -619,16 +622,18 @@
      *
      * @param zenMode one of the {@code Global#ZEN_MODE_x} values
      */
-    void applyGlobalZenModeAsImplicitZenRule(String callingPkg, int callingUid, int zenMode) {
+    void applyGlobalZenModeAsImplicitZenRule(UserHandle user, String callingPkg, int callingUid,
+            int zenMode) {
         if (!android.app.Flags.modesApi()) {
             Log.wtf(TAG, "applyGlobalZenModeAsImplicitZenRule called with flag off!");
             return;
         }
         synchronized (mConfigLock) {
-            if (mConfig == null) {
+            ZenModeConfig config = getConfigLocked(user);
+            if (config == null) {
                 return;
             }
-            ZenModeConfig newConfig = mConfig.copy();
+            ZenModeConfig newConfig = config.copy();
             ZenRule rule = newConfig.automaticRules.get(implicitRuleId(callingPkg));
             if (zenMode == Global.ZEN_MODE_OFF) {
                 // Deactivate implicit rule if it exists and is active; otherwise ignore.
@@ -650,7 +655,7 @@
                     // would apply if changing the global interruption filter. We only do this
                     // for newly created rules, as existing rules have a pre-existing policy
                     // (whether initialized here or set via app or user).
-                    rule.zenPolicy = mConfig.getZenPolicy().copy();
+                    rule.zenPolicy = config.getZenPolicy().copy();
                     newConfig.automaticRules.put(rule.id, rule);
                 }
                 // If the user has changed the rule's *zenMode*, then don't let app overwrite it.
@@ -680,17 +685,18 @@
      * {@link AutomaticZenRule#configurationActivity}. Its zen mode will be set to
      * {@link Global#ZEN_MODE_IMPORTANT_INTERRUPTIONS}.
      */
-    void applyGlobalPolicyAsImplicitZenRule(String callingPkg, int callingUid,
+    void applyGlobalPolicyAsImplicitZenRule(UserHandle user, String callingPkg, int callingUid,
             NotificationManager.Policy policy) {
         if (!android.app.Flags.modesApi()) {
             Log.wtf(TAG, "applyGlobalPolicyAsImplicitZenRule called with flag off!");
             return;
         }
         synchronized (mConfigLock) {
-            if (mConfig == null) {
+            ZenModeConfig config = getConfigLocked(user);
+            if (config == null) {
                 return;
             }
-            ZenModeConfig newConfig = mConfig.copy();
+            ZenModeConfig newConfig = config.copy();
             boolean isNew = false;
             ZenRule rule = newConfig.automaticRules.get(implicitRuleId(callingPkg));
             if (rule == null) {
@@ -709,9 +715,10 @@
                     // would take effect if changing the global policy.
                     // Note that NotificationManager.Policy cannot have any unset priority
                     // categories, but *can* have unset visual effects, which is why we do this.
-                    newZenPolicy = mConfig.getZenPolicy().overwrittenWith(newZenPolicy);
+                    newZenPolicy = config.getZenPolicy().overwrittenWith(newZenPolicy);
                 }
                 updatePolicy(
+                        newConfig,
                         rule,
                         newZenPolicy,
                         /* updateBitmask= */ false,
@@ -734,25 +741,26 @@
      * <p>Any unset values in the {@link ZenPolicy} will be mapped to their current defaults.
      */
     @Nullable
-    Policy getNotificationPolicyFromImplicitZenRule(String callingPkg) {
+    Policy getNotificationPolicyFromImplicitZenRule(UserHandle user, String callingPkg) {
         if (!android.app.Flags.modesApi()) {
             Log.wtf(TAG, "getNotificationPolicyFromImplicitZenRule called with flag off!");
-            return getNotificationPolicy();
+            return getNotificationPolicy(user);
         }
         synchronized (mConfigLock) {
-            if (mConfig == null) {
+            ZenModeConfig config = getConfigLocked(user);
+            if (config == null) {
                 return null;
             }
-            ZenRule implicitRule = mConfig.automaticRules.get(implicitRuleId(callingPkg));
+            ZenRule implicitRule = config.automaticRules.get(implicitRuleId(callingPkg));
             if (implicitRule != null && implicitRule.zenPolicy != null) {
-                // toNotificationPolicy takes defaults from mConfig, and technically, those are not
+                // toNotificationPolicy takes defaults from config, and technically those are not
                 // the defaults that would apply if any fields were unset. However, all rules should
                 // have all fields set in their ZenPolicy objects upon rule creation, so in
                 // practice, this is only filling in the areChannelsBypassingDnd field, which is a
                 // state rather than a part of the policy.
-                return mConfig.toNotificationPolicy(implicitRule.zenPolicy);
+                return config.toNotificationPolicy(implicitRule.zenPolicy);
             } else {
-                return getNotificationPolicy();
+                return getNotificationPolicy(user);
             }
         }
     }
@@ -798,13 +806,14 @@
         return rule;
     }
 
-    boolean removeAutomaticZenRule(String id, @ConfigOrigin int origin, String reason,
-            int callingUid) {
+    boolean removeAutomaticZenRule(UserHandle user, String id, @ConfigOrigin int origin,
+            String reason, int callingUid) {
         checkManageRuleOrigin("removeAutomaticZenRule", origin);
         ZenModeConfig newConfig;
         synchronized (mConfigLock) {
-            if (mConfig == null) return false;
-            newConfig = mConfig.copy();
+            ZenModeConfig config = getConfigLocked(user);
+            if (config == null) return false;
+            newConfig = config.copy();
             ZenRule ruleToRemove = newConfig.automaticRules.get(id);
             if (ruleToRemove == null) return false;
             if (canManageAutomaticZenRule(ruleToRemove)) {
@@ -826,18 +835,19 @@
                         "Cannot delete rules not owned by your condition provider");
             }
             dispatchOnAutomaticRuleStatusChanged(
-                    mConfig.user, ruleToRemove.getPkg(), id, AUTOMATIC_RULE_STATUS_REMOVED);
+                    config.user, ruleToRemove.getPkg(), id, AUTOMATIC_RULE_STATUS_REMOVED);
             return setConfigLocked(newConfig, origin, reason, null, true, callingUid);
         }
     }
 
-    boolean removeAutomaticZenRules(String packageName, @ConfigOrigin int origin,
+    boolean removeAutomaticZenRules(UserHandle user, String packageName, @ConfigOrigin int origin,
             String reason, int callingUid) {
         checkManageRuleOrigin("removeAutomaticZenRules", origin);
         ZenModeConfig newConfig;
         synchronized (mConfigLock) {
-            if (mConfig == null) return false;
-            newConfig = mConfig.copy();
+            ZenModeConfig config = getConfigLocked(user);
+            if (config == null) return false;
+            newConfig = config.copy();
             for (int i = newConfig.automaticRules.size() - 1; i >= 0; i--) {
                 ZenRule rule = newConfig.automaticRules.get(newConfig.automaticRules.keyAt(i));
                 if (Objects.equals(rule.getPkg(), packageName) && canManageAutomaticZenRule(rule)) {
@@ -885,12 +895,13 @@
     }
 
     @Condition.State
-    int getAutomaticZenRuleState(String id) {
+    int getAutomaticZenRuleState(UserHandle user, String id) {
         synchronized (mConfigLock) {
-            if (mConfig == null) {
+            ZenModeConfig config = getConfigLocked(user);
+            if (config == null) {
                 return Condition.STATE_UNKNOWN;
             }
-            ZenRule rule = mConfig.automaticRules.get(id);
+            ZenRule rule = config.automaticRules.get(id);
             if (rule == null || !canManageAutomaticZenRule(rule)) {
                 return Condition.STATE_UNKNOWN;
             }
@@ -903,14 +914,15 @@
         }
     }
 
-    void setAutomaticZenRuleState(String id, Condition condition, @ConfigOrigin int origin,
-            int callingUid) {
+    void setAutomaticZenRuleState(UserHandle user, String id, Condition condition,
+            @ConfigOrigin int origin, int callingUid) {
         checkSetRuleStateOrigin("setAutomaticZenRuleState(String id)", origin);
         ZenModeConfig newConfig;
         synchronized (mConfigLock) {
-            if (mConfig == null) return;
+            ZenModeConfig config = getConfigLocked(user);
+            if (config == null) return;
 
-            newConfig = mConfig.copy();
+            newConfig = config.copy();
             ZenRule rule = newConfig.automaticRules.get(id);
             if (Flags.modesApi()) {
                 if (rule != null && canManageAutomaticZenRule(rule)) {
@@ -925,13 +937,14 @@
         }
     }
 
-    void setAutomaticZenRuleState(Uri ruleDefinition, Condition condition,
+    void setAutomaticZenRuleState(UserHandle user, Uri ruleDefinition, Condition condition,
             @ConfigOrigin int origin, int callingUid) {
         checkSetRuleStateOrigin("setAutomaticZenRuleState(Uri ruleDefinition)", origin);
         ZenModeConfig newConfig;
         synchronized (mConfigLock) {
-            if (mConfig == null) return;
-            newConfig = mConfig.copy();
+            ZenModeConfig config = getConfigLocked(user);
+            if (config == null) return;
+            newConfig = config.copy();
 
             List<ZenRule> matchingRules = findMatchingRules(newConfig, ruleDefinition, condition);
             if (Flags.modesApi()) {
@@ -1025,13 +1038,16 @@
         return true;
     }
 
-    public int getCurrentInstanceCount(ComponentName cn) {
+    public int getCurrentInstanceCount(UserHandle user, ComponentName cn) {
         if (cn == null) {
             return 0;
         }
         int count = 0;
         synchronized (mConfigLock) {
-            for (ZenRule rule : mConfig.automaticRules.values()) {
+            ZenModeConfig config = getConfigLocked(user);
+            if (config == null) return 0;
+
+            for (ZenRule rule : config.automaticRules.values()) {
                 if (cn.equals(rule.component) || cn.equals(rule.configurationActivity)) {
                     count++;
                 }
@@ -1042,13 +1058,16 @@
 
     // Equivalent method to getCurrentInstanceCount, but for all rules associated with a specific
     // package rather than a condition provider service or activity.
-    private int getPackageRuleCount(String pkg) {
+    private int getPackageRuleCount(UserHandle user, String pkg) {
         if (pkg == null) {
             return 0;
         }
         int count = 0;
         synchronized (mConfigLock) {
-            for (ZenRule rule : mConfig.automaticRules.values()) {
+            ZenModeConfig config = getConfigLocked(user);
+            if (config == null) return 0;
+
+            for (ZenRule rule : config.automaticRules.values()) {
                 if (pkg.equals(rule.getPkg())) {
                     count++;
                 }
@@ -1081,13 +1100,15 @@
     void updateZenRulesOnLocaleChange() {
         updateRuleStringsForCurrentLocale(mContext, mDefaultConfig);
         synchronized (mConfigLock) {
-            if (mConfig == null) {
+            ZenModeConfig config = getConfigLocked(UserHandle.CURRENT);
+            if (config == null) {
                 return;
             }
-            ZenModeConfig config = mConfig.copy();
+
+            ZenModeConfig newConfig = config.copy();
             boolean updated = false;
             for (ZenRule defaultRule : mDefaultConfig.automaticRules.values()) {
-                ZenRule currRule = config.automaticRules.get(defaultRule.id);
+                ZenRule currRule = newConfig.automaticRules.get(defaultRule.id);
                 // if default rule wasn't user-modified use localized name
                 // instead of previous system name
                 if (currRule != null
@@ -1103,14 +1124,14 @@
                 }
             }
             if (Flags.modesApi() && Flags.modesUi()) {
-                for (ZenRule rule : config.automaticRules.values()) {
+                for (ZenRule rule : newConfig.automaticRules.values()) {
                     if (SystemZenRules.isSystemOwnedRule(rule)) {
                         updated |= SystemZenRules.updateTriggerDescription(mContext, rule);
                     }
                 }
             }
             if (updated) {
-                setConfigLocked(config, null, ORIGIN_SYSTEM,
+                setConfigLocked(newConfig, null, ORIGIN_SYSTEM,
                         "updateZenRulesOnLocaleChange", Process.SYSTEM_UID);
             }
         }
@@ -1170,8 +1191,8 @@
      * deactivated) unless the update has origin == {@link ZenModeConfig#ORIGIN_USER_IN_SYSTEMUI}.
      */
     @GuardedBy("mConfigLock")
-    private boolean populateZenRule(String pkg, AutomaticZenRule azr, ZenRule rule,
-                         @ConfigOrigin int origin, boolean isNew) {
+    private boolean populateZenRule(String pkg, AutomaticZenRule azr, ZenModeConfig config,
+            ZenRule rule, @ConfigOrigin int origin, boolean isNew) {
         if (Flags.modesApi()) {
             boolean modified = false;
             // These values can always be edited by the app, so we apply changes immediately.
@@ -1307,7 +1328,7 @@
             }
 
             // Updates the bitmask and values for all policy fields, based on the origin.
-            modified |= updatePolicy(rule, azr.getZenPolicy(), updateBitmask, isNew);
+            modified |= updatePolicy(config, rule, azr.getZenPolicy(), updateBitmask, isNew);
 
             // Updates the bitmask and values for all device effect fields, based on the origin.
             modified |= updateZenDeviceEffects(rule, azr.getDeviceEffects(),
@@ -1360,13 +1381,13 @@
      * <p>Returns {@code true} if the policy of the rule was modified.
      */
     @GuardedBy("mConfigLock")
-    private boolean updatePolicy(ZenRule zenRule, @Nullable ZenPolicy newPolicy,
-            boolean updateBitmask, boolean isNew) {
+    private boolean updatePolicy(ZenModeConfig config, ZenRule zenRule,
+            @Nullable ZenPolicy newPolicy, boolean updateBitmask, boolean isNew) {
         if (newPolicy == null) {
             if (isNew) {
                 // Newly created rule with no provided policy; fill in with the default.
                 zenRule.zenPolicy =
-                        (Flags.modesUi() ? mDefaultConfig.getZenPolicy() : mConfig.getZenPolicy())
+                        (Flags.modesUi() ? mDefaultConfig.getZenPolicy() : config.getZenPolicy())
                                 .copy();
                 return true;
             }
@@ -1378,7 +1399,7 @@
         // fields in the bitmask should be marked as updated.
         ZenPolicy oldPolicy = zenRule.zenPolicy != null
                 ? zenRule.zenPolicy
-                : (Flags.modesUi() ? mDefaultConfig.getZenPolicy() : mConfig.getZenPolicy());
+                : (Flags.modesUi() ? mDefaultConfig.getZenPolicy() : config.getZenPolicy());
 
         // If this is updating a rule rather than creating a new one, keep any fields from the
         // old policy if they are unspecified in the new policy. For newly created rules, oldPolicy
@@ -1570,17 +1591,20 @@
     // Update only the hasPriorityChannels state (aka areChannelsBypassingDnd) without modifying
     // any of the rest of the existing policy. This allows components that only want to modify
     // this bit (PreferencesHelper) to not have to adjust the rest of the policy.
-    protected void updateHasPriorityChannels(boolean hasPriorityChannels) {
+    protected void updateHasPriorityChannels(UserHandle user, boolean hasPriorityChannels) {
         if (!Flags.modesUi()) {
             Log.wtf(TAG, "updateHasPriorityChannels called without modes_ui");
         }
         synchronized (mConfigLock) {
+            ZenModeConfig config = getConfigLocked(user);
+            if (config == null) return;
+
             // If it already matches, do nothing
-            if (mConfig.areChannelsBypassingDnd == hasPriorityChannels) {
+            if (config.areChannelsBypassingDnd == hasPriorityChannels) {
                 return;
             }
 
-            ZenModeConfig newConfig = mConfig.copy();
+            ZenModeConfig newConfig = config.copy();
             newConfig.areChannelsBypassingDnd = hasPriorityChannels;
             // The updated calculation of whether there are priority channels is always done by
             // the system, even if the event causing the calculation had a different origin.
@@ -1610,22 +1634,25 @@
                 : AUTOMATIC_RULE_STATUS_DISABLED);
     }
 
-    void setManualZenMode(int zenMode, Uri conditionId, @ConfigOrigin int origin,
+    void setManualZenMode(UserHandle user, int zenMode, Uri conditionId, @ConfigOrigin int origin,
             String reason, @Nullable String caller, int callingUid) {
-        setManualZenMode(zenMode, conditionId, origin, reason, caller, true /*setRingerMode*/,
+        setManualZenMode(user, zenMode, conditionId, origin, reason, caller, true /*setRingerMode*/,
                 callingUid);
     }
 
-    private void setManualZenMode(int zenMode, Uri conditionId, @ConfigOrigin int origin,
-            String reason, @Nullable String caller, boolean setRingerMode, int callingUid) {
+    private void setManualZenMode(UserHandle user, int zenMode, Uri conditionId,
+            @ConfigOrigin int origin, String reason, @Nullable String caller, boolean setRingerMode,
+            int callingUid) {
         ZenModeConfig newConfig;
         synchronized (mConfigLock) {
-            if (mConfig == null) return;
+            ZenModeConfig config = getConfigLocked(user);
+            if (config == null) return;
+
             if (!Global.isValidZenMode(zenMode)) return;
             if (DEBUG) Log.d(TAG, "setManualZenMode " + Global.zenModeToString(zenMode)
                     + " conditionId=" + conditionId + " reason=" + reason
                     + " setRingerMode=" + setRingerMode);
-            newConfig = mConfig.copy();
+            newConfig = config.copy();
             if (Flags.modesUi()) {
                 newConfig.manualRule.enabler = caller;
                 newConfig.manualRule.conditionId = conditionId != null ? conditionId : Uri.EMPTY;
@@ -1668,18 +1695,20 @@
         }
     }
 
-    public void setManualZenRuleDeviceEffects(ZenDeviceEffects deviceEffects,
+    public void setManualZenRuleDeviceEffects(UserHandle user, ZenDeviceEffects deviceEffects,
             @ConfigOrigin int origin, String reason, int callingUid) {
         if (!Flags.modesUi()) {
             return;
         }
         ZenModeConfig newConfig;
         synchronized (mConfigLock) {
-            if (mConfig == null) return;
+            ZenModeConfig config = getConfigLocked(user);
+            if (config == null) return;
+
             if (DEBUG) Log.d(TAG, "updateManualRule " + deviceEffects
                     + " reason=" + reason
                     + " callingUid=" + callingUid);
-            newConfig = mConfig.copy();
+            newConfig = config.copy();
 
             newConfig.manualRule.pkg = PACKAGE_ANDROID;
             newConfig.manualRule.zenDeviceEffects = deviceEffects;
@@ -1709,7 +1738,7 @@
         pw.println(Global.zenModeToString(mZenMode));
         pw.print(prefix);
         pw.println("mConsolidatedPolicy=" + mConsolidatedPolicy.toString());
-        synchronized (mConfigsArrayLock) {
+        synchronized (mConfigLock) {
             final int N = mConfigs.size();
             for (int i = 0; i < N; i++) {
                 dump(pw, prefix, "mConfigs[u=" + mConfigs.keyAt(i) + "]", mConfigs.valueAt(i));
@@ -1835,7 +1864,7 @@
 
     public void writeXml(TypedXmlSerializer out, boolean forBackup, Integer version, int userId)
             throws IOException {
-        synchronized (mConfigsArrayLock) {
+        synchronized (mConfigLock) {
             final int n = mConfigs.size();
             for (int i = 0; i < n; i++) {
                 if (forBackup && mConfigs.keyAt(i) != userId) {
@@ -1849,24 +1878,29 @@
     /**
      * @return user-specified default notification policy for priority only do not disturb
      */
-    public Policy getNotificationPolicy() {
+    @Nullable
+    public Policy getNotificationPolicy(UserHandle user) {
         synchronized (mConfigLock) {
-            return getNotificationPolicy(mConfig);
+            return getNotificationPolicy(getConfigLocked(user));
         }
     }
 
-    private static Policy getNotificationPolicy(ZenModeConfig config) {
+    @Nullable
+    private static Policy getNotificationPolicy(@Nullable ZenModeConfig config) {
         return config == null ? null : config.toNotificationPolicy();
     }
 
     /**
      * Sets the global notification policy used for priority only do not disturb
      */
-    public void setNotificationPolicy(Policy policy, @ConfigOrigin int origin,
+    public void setNotificationPolicy(UserHandle user, Policy policy, @ConfigOrigin int origin,
             int callingUid) {
         synchronized (mConfigLock) {
-            if (policy == null || mConfig == null) return;
-            final ZenModeConfig newConfig = mConfig.copy();
+            if (policy == null) return;
+            ZenModeConfig config = getConfigLocked(user);
+            if (config == null) return;
+
+            final ZenModeConfig newConfig = config.copy();
             if (Flags.modesApi() && !Flags.modesUi()) {
                 // Fix for b/337193321 -- propagate changes to notificationPolicy to rules where
                 // the user cannot edit zen policy to emulate the previous "inheritance".
@@ -1894,7 +1928,7 @@
     }
 
     /**
-     * Cleans up obsolete rules:
+     * Cleans up obsolete rules in the current {@link ZenModeConfig}.
      * <ul>
      *     <li>Rule instances whose owner is not installed.
      *     <li>Deleted rules that were deleted more than 30 days ago.
@@ -1966,6 +2000,27 @@
         return mDefaultConfig.getZenPolicy();
     }
 
+    /**
+     * Returns the {@link ZenModeConfig} corresponding to the supplied {@link UserHandle}.
+     * The result will be {@link #mConfig} if the user is {@link UserHandle#CURRENT}, or the same
+     * as {@link #mUser}, otherwise will be the corresponding entry in {@link #mConfigs}.
+     *
+     * <p>Remember to continue holding {@link #mConfigLock} while operating on the returned value.
+     */
+    @Nullable
+    @GuardedBy("mConfigLock")
+    private ZenModeConfig getConfigLocked(@NonNull UserHandle user) {
+        if (Flags.modesMultiuser()) {
+            if (user.getIdentifier() == UserHandle.USER_CURRENT || user.getIdentifier() == mUser) {
+                return mConfig;
+            } else {
+                return mConfigs.get(user.getIdentifier());
+            }
+        } else {
+            return mConfig;
+        }
+    }
+
     @GuardedBy("mConfigLock")
     private boolean setConfigLocked(ZenModeConfig config, ComponentName triggeringComponent,
             @ConfigOrigin int origin, String reason, int callingUid) {
@@ -1992,7 +2047,7 @@
             }
             if (config.user != mUser) {
                 // simply store away for background users
-                synchronized (mConfigsArrayLock) {
+                synchronized (mConfigLock) {
                     mConfigs.put(config.user, config);
                 }
                 if (DEBUG) Log.d(TAG, "setConfigLocked: store config for user " + config.user);
@@ -2001,7 +2056,7 @@
             // handle CPS backed conditions - danger! may modify config
             mConditions.evaluateConfig(config, null, false /*processSubscriptions*/);
 
-            synchronized (mConfigsArrayLock) {
+            synchronized (mConfigLock) {
                 mConfigs.put(config.user, config);
             }
             if (DEBUG) Log.d(TAG, "setConfigLocked reason=" + reason, new Throwable());
@@ -2142,7 +2197,8 @@
     }
 
     @GuardedBy("mConfigLock")
-    private void applyCustomPolicy(ZenPolicy policy, ZenRule rule, boolean useManualConfig) {
+    private void applyCustomPolicy(ZenModeConfig config, ZenPolicy policy, ZenRule rule,
+            boolean useManualConfig) {
         if (rule.zenMode == Global.ZEN_MODE_NO_INTERRUPTIONS) {
             if (Flags.modesApi() && Flags.modesUi()) {
                 policy.apply(ZenPolicy.getBasePolicyInterruptionFilterNone());
@@ -2168,8 +2224,8 @@
         } else {
             if (Flags.modesApi()) {
                 if (useManualConfig) {
-                    // manual rule is configured using the settings stored directly in mConfig
-                    policy.apply(mConfig.getZenPolicy());
+                    // manual rule is configured using the settings stored directly in ZenModeConfig
+                    policy.apply(config.getZenPolicy());
                 } else {
                     // under modes_api flag, an active automatic rule with no specified policy
                     // inherits the device default settings as stored in mDefaultConfig. While the
@@ -2177,11 +2233,11 @@
                     // catch any that may have fallen through the cracks.
                     Log.wtf(TAG, "active automatic rule found with no specified policy: " + rule);
                     policy.apply(Flags.modesUi()
-                            ? mDefaultConfig.getZenPolicy() : mConfig.getZenPolicy());
+                            ? mDefaultConfig.getZenPolicy() : config.getZenPolicy());
                 }
             } else {
                 // active rule with no specified policy inherits the manual rule config settings
-                policy.apply(mConfig.getZenPolicy());
+                policy.apply(config.getZenPolicy());
             }
         }
     }
@@ -2194,7 +2250,7 @@
             ZenPolicy policy = new ZenPolicy();
             ZenDeviceEffects.Builder deviceEffectsBuilder = new ZenDeviceEffects.Builder();
             if (mConfig.isManualActive()) {
-                applyCustomPolicy(policy, mConfig.manualRule, true);
+                applyCustomPolicy(mConfig, policy, mConfig.manualRule, true);
                 if (Flags.modesApi()) {
                     deviceEffectsBuilder.add(mConfig.manualRule.zenDeviceEffects);
                 }
@@ -2206,7 +2262,7 @@
                     // policy. This is relevant in case some other active rule has a more
                     // restrictive INTERRUPTION_FILTER but a more lenient ZenPolicy!
                     if (!Flags.modesApi() || automaticRule.zenMode != Global.ZEN_MODE_OFF) {
-                        applyCustomPolicy(policy, automaticRule, false);
+                        applyCustomPolicy(mConfig, policy, automaticRule, false);
                     }
                     if (Flags.modesApi()) {
                         deviceEffectsBuilder.add(automaticRule.zenDeviceEffects);
@@ -2469,7 +2525,7 @@
      * Generate pulled atoms about do not disturb configurations.
      */
     public void pullRules(List<StatsEvent> events) {
-        synchronized (mConfigsArrayLock) {
+        synchronized (mConfigLock) {
             final int numConfigs = mConfigs.size();
             for (int i = 0; i < numConfigs; i++) {
                 final int user = mConfigs.keyAt(i);
@@ -2496,7 +2552,7 @@
         }
     }
 
-    @GuardedBy("mConfigsArrayLock")
+    @GuardedBy("mConfigLock")
     private void ruleToProtoLocked(int user, ZenRule rule, boolean isManualRule,
             List<StatsEvent> events) {
         // Make the ID safe.
@@ -2601,7 +2657,7 @@
             }
 
             if (newZen != -1) {
-                setManualZenMode(newZen, null, ORIGIN_SYSTEM,
+                setManualZenMode(UserHandle.CURRENT, newZen, null, ORIGIN_SYSTEM,
                         "ringerModeInternal", /* caller= */ null, /* setRingerMode= */ false,
                         Process.SYSTEM_UID);
             }
@@ -2646,7 +2702,7 @@
                     break;
             }
             if (newZen != -1) {
-                setManualZenMode(newZen, null, ORIGIN_SYSTEM,
+                setManualZenMode(UserHandle.CURRENT, newZen, null, ORIGIN_SYSTEM,
                         "ringerModeExternal", caller, false /*setRingerMode*/, Process.SYSTEM_UID);
             }
 
diff --git a/services/core/java/com/android/server/pm/BackgroundUserSoundNotifier.java b/services/core/java/com/android/server/pm/BackgroundUserSoundNotifier.java
index d1d6ed0..77572e0 100644
--- a/services/core/java/com/android/server/pm/BackgroundUserSoundNotifier.java
+++ b/services/core/java/com/android/server/pm/BackgroundUserSoundNotifier.java
@@ -179,13 +179,17 @@
                     final String action = intent.getAction().substring(actionIndex);
                     Log.d(LOG_TAG, "Action requested: " + action + ", by userId "
                             + ActivityManager.getCurrentUser() + " for alarm on user "
-                            + UserHandle.getUserHandleForUid(clientUid));
+                            + UserHandle.getUserHandleForUid(clientUid).getIdentifier());
                 }
 
                 if (ACTION_MUTE_SOUND.equals(intent.getAction())) {
                     muteAlarmSounds(clientUid);
                 } else if (ACTION_SWITCH_USER.equals(intent.getAction())) {
-                    activityManager.switchUser(UserHandle.getUserId(clientUid));
+                    int userId = UserHandle.getUserId(clientUid);
+                    if (mUserManager.isProfile(userId)) {
+                        userId = mUserManager.getProfileParent(userId).id;
+                    }
+                    activityManager.switchUser(userId);
                 }
                 if (Flags.multipleAlarmNotificationsSupport()) {
                     mNotificationClientUids.remove(clientUid);
@@ -237,11 +241,12 @@
                 UserHandle.of(ActivityManager.getCurrentUser()), 0);
         final int userId = UserHandle.getUserId(afi.getClientUid());
         final int usage = afi.getAttributes().getUsage();
-        UserInfo userInfo = mUserManager.getUserInfo(userId);
-
+        UserInfo userInfo = mUserManager.isProfile(userId) ? mUserManager.getProfileParent(userId) :
+                mUserManager.getUserInfo(userId);
+        ActivityManager activityManager = foregroundContext.getSystemService(ActivityManager.class);
         // Only show notification if the sound is coming from background user and the notification
         // for this UID is not already shown.
-        if (userInfo != null && userId != foregroundContext.getUserId()
+        if (userInfo != null && !activityManager.isProfileForeground(userInfo.getUserHandle())
                 && !isNotificationShown(afi.getClientUid())) {
             //TODO: b/349138482 - Add handling of cases when usage == USAGE_NOTIFICATION_RINGTONE
             if (usage == USAGE_ALARM) {
diff --git a/services/core/java/com/android/server/pm/ComputerEngine.java b/services/core/java/com/android/server/pm/ComputerEngine.java
index 19ac1ec..58b1e49 100644
--- a/services/core/java/com/android/server/pm/ComputerEngine.java
+++ b/services/core/java/com/android/server/pm/ComputerEngine.java
@@ -71,6 +71,7 @@
 import android.app.admin.DevicePolicyManagerInternal;
 import android.companion.virtual.VirtualDeviceManager;
 import android.content.ComponentName;
+import android.content.ContentProvider;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
@@ -649,11 +650,11 @@
             int userId, int callingUid, int callingPid,
             boolean includeInstantApps, boolean resolveForStart) {
         if (!mUserManager.exists(userId)) return Collections.emptyList();
-        enforceCrossUserOrProfilePermission(callingUid,
+        enforceCrossUserOrProfilePermission(Binder.getCallingUid(),
                 userId,
                 false /*requireFullPermission*/,
                 false /*checkShell*/,
-                "query intent receivers");
+                "query intent services");
         final String instantAppPkgName = getInstantAppPackageName(callingUid);
         flags = updateFlagsForResolve(flags, userId, callingUid, includeInstantApps,
                 false /* isImplicitImageCaptureIntentAndNotSetByDpc */);
@@ -2208,10 +2209,10 @@
             return true;
         }
         boolean permissionGranted = requireFullPermission ? hasPermission(
-                Manifest.permission.INTERACT_ACROSS_USERS_FULL)
+                Manifest.permission.INTERACT_ACROSS_USERS_FULL, callingUid)
                 : (hasPermission(
-                        android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
-                        || hasPermission(Manifest.permission.INTERACT_ACROSS_USERS));
+                        android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, callingUid)
+                        || hasPermission(Manifest.permission.INTERACT_ACROSS_USERS, callingUid));
         if (!permissionGranted) {
             if (Process.isIsolatedUid(callingUid) && isKnownIsolatedComputeApp(callingUid)) {
                 return checkIsolatedOwnerHasPermission(callingUid, requireFullPermission);
@@ -4669,7 +4670,7 @@
 
         if (!forceAllowCrossUser) {
             enforceCrossUserPermission(
-                    callingUid,
+                    Binder.getCallingUid(),
                     userId,
                     false /* requireFullPermission */,
                     false /* checkShell */,
@@ -4752,8 +4753,14 @@
             int callingUid) {
         if (!mUserManager.exists(userId)) return null;
         flags = updateFlagsForComponent(flags, userId);
-        final ProviderInfo providerInfo = mComponentResolver.queryProvider(this, name, flags,
-                userId);
+
+        // Callers of this API may not always separate the userID and authority. Let's parse it
+        // before resolving
+        String authorityWithoutUserId = ContentProvider.getAuthorityWithoutUserId(name);
+        userId = ContentProvider.getUserIdFromAuthority(name, userId);
+
+        final ProviderInfo providerInfo = mComponentResolver.queryProvider(this,
+                authorityWithoutUserId, flags, userId);
         boolean checkedGrants = false;
         if (providerInfo != null) {
             // Looking for cross-user grants before enforcing the typical cross-users permissions
@@ -4767,7 +4774,7 @@
         if (!checkedGrants) {
             boolean enforceCrossUser = true;
 
-            if (isAuthorityRedirectedForCloneProfile(name)) {
+            if (isAuthorityRedirectedForCloneProfile(authorityWithoutUserId)) {
                 final UserManagerInternal umInternal = mInjector.getUserManagerInternal();
 
                 UserInfo userInfo = umInternal.getUserInfo(UserHandle.getUserId(callingUid));
@@ -5242,7 +5249,7 @@
     @Override
     public int getComponentEnabledSetting(@NonNull ComponentName component, int callingUid,
             @UserIdInt int userId) {
-        enforceCrossUserPermission(callingUid, userId, false /*requireFullPermission*/,
+        enforceCrossUserPermission(Binder.getCallingUid(), userId, false /*requireFullPermission*/,
                 false /*checkShell*/, "getComponentEnabled");
         return getComponentEnabledSettingInternal(component, callingUid, userId);
     }
diff --git a/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFiltersUtils.java b/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFiltersUtils.java
index 5ac883c..9c24abc 100644
--- a/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFiltersUtils.java
+++ b/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFiltersUtils.java
@@ -316,6 +316,8 @@
                     /* letsPersonalDataIntoProfile= */ true)
                     .addAction(MediaStore.ACTION_IMAGE_CAPTURE)
                     .addAction(MediaStore.ACTION_IMAGE_CAPTURE_SECURE)
+                    .addAction(MediaStore.ACTION_MOTION_PHOTO_CAPTURE)
+                    .addAction(MediaStore.ACTION_MOTION_PHOTO_CAPTURE_SECURE)
                     .addAction(MediaStore.ACTION_VIDEO_CAPTURE)
                     .addAction(MediaStore.Audio.Media.RECORD_SOUND_ACTION)
                     .addAction(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA)
@@ -438,6 +440,8 @@
                     /* letsPersonalDataIntoProfile= */ false)
                     .addAction(MediaStore.ACTION_IMAGE_CAPTURE)
                     .addAction(MediaStore.ACTION_IMAGE_CAPTURE_SECURE)
+                    .addAction(MediaStore.ACTION_MOTION_PHOTO_CAPTURE)
+                    .addAction(MediaStore.ACTION_MOTION_PHOTO_CAPTURE_SECURE)
                     .addAction(MediaStore.ACTION_VIDEO_CAPTURE)
                     .addAction(MediaStore.Audio.Media.RECORD_SOUND_ACTION)
                     .addAction(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA)
diff --git a/services/core/java/com/android/server/pm/PackageInstallerHistoricalSession.java b/services/core/java/com/android/server/pm/PackageInstallerHistoricalSession.java
index ea37d8e..99eac37 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerHistoricalSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerHistoricalSession.java
@@ -79,6 +79,8 @@
     private final String mSessionErrorMessage;
     private final String mPreVerifiedDomains;
     private final String mPackageName;
+    private final int mInitialVerificationPolicy;
+    private final int mCurrentVerificationPolicy;
 
     PackageInstallerHistoricalSession(int sessionId, int userId, int originalInstallerUid,
             String originalInstallerPackageName, InstallSource installSource, int installerUid,
@@ -90,7 +92,8 @@
             int[] childSessionIds, boolean sessionApplied, boolean sessionFailed,
             boolean sessionReady, int sessionErrorCode, String sessionErrorMessage,
             PreapprovalDetails preapprovalDetails, DomainSet preVerifiedDomains,
-            String packageNameFromApk) {
+            String packageNameFromApk, int initialVerificationPolicy,
+            int currentVerificationPolicy) {
         this.sessionId = sessionId;
         this.userId = userId;
         this.mOriginalInstallerUid = originalInstallerUid;
@@ -140,6 +143,8 @@
 
         this.mPackageName = preapprovalDetails != null ? preapprovalDetails.getPackageName()
                 : packageNameFromApk != null ? packageNameFromApk : params.appPackageName;
+        this.mInitialVerificationPolicy = initialVerificationPolicy;
+        this.mCurrentVerificationPolicy = currentVerificationPolicy;
     }
 
     void dump(IndentingPrintWriter pw) {
@@ -184,6 +189,8 @@
         pw.printPair("mPreapprovalDetails", mPreapprovalDetails);
         pw.printPair("mPreVerifiedDomains", mPreVerifiedDomains);
         pw.printPair("mAppPackageName", mPackageName);
+        pw.printPair("mInitialVerificationPolicy", mInitialVerificationPolicy);
+        pw.printPair("mCurrentVerificationPolicy", mCurrentVerificationPolicy);
         pw.println();
 
         pw.decreaseIndent();
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index f6a808b..ef09976 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -30,6 +30,7 @@
 import static android.content.pm.PackageManager.INSTALL_UNARCHIVE_DRAFT;
 import static android.os.Process.INVALID_UID;
 import static android.os.Process.SYSTEM_UID;
+import static android.os.UserHandle.USER_SYSTEM;
 
 import static com.android.server.pm.PackageArchiver.isArchivingEnabled;
 import static com.android.server.pm.PackageInstallerSession.isValidVerificationPolicy;
@@ -39,6 +40,7 @@
 import static org.xmlpull.v1.XmlPullParser.START_TAG;
 
 import android.Manifest;
+import android.annotation.EnforcePermission;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.ActivityManager;
@@ -87,6 +89,7 @@
 import android.os.Looper;
 import android.os.Message;
 import android.os.ParcelableException;
+import android.os.PermissionEnforcer;
 import android.os.Process;
 import android.os.RemoteCallback;
 import android.os.RemoteCallbackList;
@@ -152,7 +155,6 @@
 import java.util.TreeMap;
 import java.util.TreeSet;
 import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.atomic.AtomicInteger;
 import java.util.function.IntPredicate;
 import java.util.function.Supplier;
 
@@ -279,11 +281,12 @@
     };
 
     /**
-     * Default verification policy for incoming installation sessions.
-     * TODO(b/360129657): update the default policy.
+     * Default verification policy for incoming installation sessions, mapped from userId to policy.
      */
-    private final AtomicInteger mVerificationPolicy = new AtomicInteger(
-            VERIFICATION_POLICY_BLOCK_FAIL_WARN);
+    @GuardedBy("mVerificationPolicyPerUser")
+    private final SparseIntArray mVerificationPolicyPerUser = new SparseIntArray(1);
+    // TODO(b/360129657): update the default policy.
+    private static final int DEFAULT_VERIFICATION_POLICY = VERIFICATION_POLICY_BLOCK_FAIL_WARN;
 
     private static final class Lifecycle extends SystemService {
         private final PackageInstallerService mPackageInstallerService;
@@ -314,6 +317,8 @@
 
     public PackageInstallerService(Context context, PackageManagerService pm,
             Supplier<PackageParser2> apexParserSupplier) {
+        super(PermissionEnforcer.fromContext(context));
+
         mContext = context;
         mPm = pm;
 
@@ -338,6 +343,9 @@
                 context, mInstallThread.getLooper(), new AppStateHelper(context));
         mPackageArchiver = new PackageArchiver(mContext, mPm);
         mVerifierController = new VerifierController(mContext, mInstallHandler);
+        synchronized (mVerificationPolicyPerUser) {
+            mVerificationPolicyPerUser.put(USER_SYSTEM, DEFAULT_VERIFICATION_POLICY);
+        }
 
         LocalServices.getService(SystemServiceManager.class).startService(
                 new Lifecycle(context, this));
@@ -1047,12 +1055,17 @@
         InstallSource installSource = InstallSource.create(installerPackageName,
                 originatingPackageName, requestedInstallerPackageName, requestedInstallerPackageUid,
                 requestedInstallerPackageName, installerAttributionTag, params.packageSource);
+        final int verificationPolicy;
+        synchronized (mVerificationPolicyPerUser) {
+            verificationPolicy = mVerificationPolicyPerUser.get(
+                    userId, DEFAULT_VERIFICATION_POLICY);
+        }
         session = new PackageInstallerSession(mInternalCallback, mContext, mPm, this,
                 mSilentUpdatePolicy, mInstallThread.getLooper(), mStagingManager, sessionId,
                 userId, callingUid, installSource, params, createdMillis, 0L, stageDir, stageCid,
                 null, null, false, false, false, false, null, SessionInfo.INVALID_ID,
                 false, false, false, PackageManager.INSTALL_UNKNOWN, "", null,
-                mVerifierController, mVerificationPolicy.get());
+                mVerifierController, verificationPolicy, verificationPolicy);
 
         synchronized (mSessions) {
             mSessions.put(sessionId, session);
@@ -1877,33 +1890,57 @@
     }
 
     @Override
-    public @PackageInstaller.VerificationPolicy int getVerificationPolicy() {
-        if (mContext.checkCallingOrSelfPermission(Manifest.permission.VERIFICATION_AGENT)
-                != PackageManager.PERMISSION_GRANTED) {
-            throw new SecurityException("You need the "
-                    + "com.android.permission.VERIFICATION_AGENT permission "
-                    + "to get the verification policy");
+    @EnforcePermission(android.Manifest.permission.VERIFICATION_AGENT)
+    public @PackageInstaller.VerificationPolicy int getVerificationPolicy(int userId) {
+        getVerificationPolicy_enforcePermission();
+        synchronized (mVerificationPolicyPerUser) {
+            if (mVerificationPolicyPerUser.indexOfKey(userId) < 0) {
+                throw new IllegalStateException(
+                        "Verification policy for user " + userId + " does not exist."
+                                + " Does the user exist?");
+            }
+            return mVerificationPolicyPerUser.get(userId);
         }
-        return mVerificationPolicy.get();
     }
 
     @Override
-    public boolean setVerificationPolicy(@PackageInstaller.VerificationPolicy int policy) {
-        if (mContext.checkCallingOrSelfPermission(Manifest.permission.VERIFICATION_AGENT)
-                != PackageManager.PERMISSION_GRANTED) {
-            throw new SecurityException("You need the "
-                    + "com.android.permission.VERIFICATION_AGENT permission "
-                    + "to set the verification policy");
+    @EnforcePermission(android.Manifest.permission.VERIFICATION_AGENT)
+    public boolean setVerificationPolicy(@PackageInstaller.VerificationPolicy int policy,
+            int userId) {
+        setVerificationPolicy_enforcePermission();
+        final int callingUid = getCallingUid();
+        // Only the verifier currently bound by the system can change the policy, except for Shell
+        if (!PackageManagerServiceUtils.isRootOrShell(callingUid)) {
+            mVerifierController.assertCallerIsCurrentVerifier(callingUid);
         }
         if (!isValidVerificationPolicy(policy)) {
             return false;
         }
-        if (policy != mVerificationPolicy.get()) {
-            mVerificationPolicy.set(policy);
+        synchronized (mVerificationPolicyPerUser) {
+            if (mVerificationPolicyPerUser.indexOfKey(userId) < 0) {
+                throw new IllegalStateException(
+                        "Verification policy for user " + userId + " does not exist."
+                                + " Does the user exist?");
+            }
+            if (policy != mVerificationPolicyPerUser.get(userId)) {
+                mVerificationPolicyPerUser.put(userId, policy);
+            }
         }
         return true;
     }
 
+    void onUserAdded(int userId) {
+        synchronized (mVerificationPolicyPerUser) {
+            mVerificationPolicyPerUser.put(userId, DEFAULT_VERIFICATION_POLICY);
+        }
+    }
+
+    void onUserRemoved(int userId) {
+        synchronized (mVerificationPolicyPerUser) {
+            mVerificationPolicyPerUser.delete(userId);
+        }
+    }
+
     private static int getSessionCount(SparseArray<PackageInstallerSession> sessions,
             int installerUid) {
         int count = 0;
@@ -2300,6 +2337,9 @@
         }
         mSilentUpdatePolicy.dump(pw);
         mGentleUpdateHelper.dump(pw);
+        synchronized (mVerificationPolicyPerUser) {
+            pw.printPair("VerificationPolicyPerUser", mVerificationPolicyPerUser.toString());
+        }
     }
 
     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 9a9e434..2a92de5 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -320,7 +320,8 @@
     private static final String ATTR_APPLICATION_ENABLED_SETTING_PERSISTENT =
             "applicationEnabledSettingPersistent";
     private static final String ATTR_DOMAIN = "domain";
-    private static final String ATTR_VERIFICATION_POLICY = "verificationPolicy";
+    private static final String ATTR_INITIAL_VERIFICATION_POLICY = "initialVerificationPolicy";
+    private static final String ATTR_CURRENT_VERIFICATION_POLICY = "currentVerificationPolicy";
 
     private static final String PROPERTY_NAME_INHERIT_NATIVE = "pi.inherit_native_on_dont_kill";
     private static final int[] EMPTY_CHILD_SESSION_ARRAY = EmptyArray.INT;
@@ -418,10 +419,14 @@
     private final PackageSessionProvider mSessionProvider;
     private final SilentUpdatePolicy mSilentUpdatePolicy;
     /**
-     * The verification policy applied to this session, which might be different from the default
-     * verification policy used by the system.
+     * The initial verification policy assigned to this session when it was first created.
      */
-    private final AtomicInteger mVerificationPolicy;
+    private final int mInitialVerificationPolicy;
+    /**
+     * The active verification policy, which might be different from the initial verification policy
+     * assigned to this session or the default policy currently used by the system.
+     */
+    private final AtomicInteger mCurrentVerificationPolicy;
     /**
      * Note all calls must be done outside {@link #mLock} to prevent lock inversion.
      */
@@ -1182,7 +1187,8 @@
             boolean isFailed, boolean isApplied, int sessionErrorCode,
             String sessionErrorMessage, DomainSet preVerifiedDomains,
             @NonNull VerifierController verifierController,
-            @PackageInstaller.VerificationPolicy int verificationPolicy) {
+            @PackageInstaller.VerificationPolicy int initialVerificationPolicy,
+            @PackageInstaller.VerificationPolicy int currentVerificationPolicy) {
         mCallback = callback;
         mContext = context;
         mPm = pm;
@@ -1192,7 +1198,8 @@
         mHandler = new Handler(looper, mHandlerCallback);
         mStagingManager = stagingManager;
         mVerifierController = verifierController;
-        mVerificationPolicy = new AtomicInteger(verificationPolicy);
+        mInitialVerificationPolicy = initialVerificationPolicy;
+        mCurrentVerificationPolicy = new AtomicInteger(currentVerificationPolicy);
 
         this.sessionId = sessionId;
         this.userId = userId;
@@ -1302,7 +1309,8 @@
                     mStageDirInUse, mDestroyed, mFds.size(), mBridges.size(), mFinalStatus,
                     mFinalMessage, params, mParentSessionId, getChildSessionIdsLocked(),
                     mSessionApplied, mSessionFailed, mSessionReady, mSessionErrorCode,
-                    mSessionErrorMessage, mPreapprovalDetails, mPreVerifiedDomains, mPackageName);
+                    mSessionErrorMessage, mPreapprovalDetails, mPreVerifiedDomains, mPackageName,
+                    mInitialVerificationPolicy, mCurrentVerificationPolicy.get());
         }
     }
 
@@ -2884,14 +2892,13 @@
             }
             // Send the request to the verifier and wait for its response before the rest of
             // the installation can proceed.
+            final VerifierCallback verifierCallback = new VerifierCallback();
             if (!mVerifierController.startVerificationSession(mPm::snapshotComputer, userId,
                     sessionId, getPackageName(), Uri.fromFile(stageDir), signingInfo,
-                    declaredLibraries, mVerificationPolicy.get(), /* extensionParams= */ null,
-                    new VerifierCallback(), /* retry= */ false)) {
-                // A verifier is installed but cannot be connected. Installation disallowed.
-                onSessionVerificationFailure(INSTALL_FAILED_INTERNAL_ERROR,
-                        "A verifier agent is available on device but cannot be connected.",
-                        /* extras= */ null);
+                    declaredLibraries, mCurrentVerificationPolicy.get(),
+                    /* extensionParams= */ null, verifierCallback, /* retry= */ false)) {
+                // A verifier is installed but cannot be connected.
+                verifierCallback.onConnectionFailed();
             }
         } else {
             // No need to check with verifier. Proceed with the rest of the verification.
@@ -2968,7 +2975,7 @@
          * verification policy for this session.
          */
         public @PackageInstaller.VerificationPolicy int getVerificationPolicy() {
-            return mVerificationPolicy.get();
+            return mCurrentVerificationPolicy.get();
         }
         /**
          * Called by the VerifierController when the verifier requests to change the verification
@@ -2978,7 +2985,7 @@
             if (!isValidVerificationPolicy(policy)) {
                 return false;
             }
-            mVerificationPolicy.set(policy);
+            mCurrentVerificationPolicy.set(policy);
             return true;
         }
         /**
@@ -2995,7 +3002,6 @@
                         onSessionVerificationFailure(INSTALL_FAILED_VERIFICATION_FAILURE,
                                 "A verifier agent is available on device but cannot be connected.",
                                 bundle);
-
                     });
         }
         /**
@@ -3025,7 +3031,7 @@
             // TODO: handle extension response
             mHandler.post(() -> {
                 if (statusReceived.isVerified()
-                        || mVerificationPolicy.get() == VERIFICATION_POLICY_NONE) {
+                        || mCurrentVerificationPolicy.get() == VERIFICATION_POLICY_NONE) {
                     // Continue with the rest of the verification and installation.
                     resumeVerify();
                     return;
@@ -3069,13 +3075,14 @@
         }
 
         private void handleNonPackageBlockedFailure(Runnable onFailWarning, Runnable onFailClosed) {
-            final Runnable r = switch (mVerificationPolicy.get()) {
+            final Runnable r = switch (mCurrentVerificationPolicy.get()) {
                 case VERIFICATION_POLICY_NONE, VERIFICATION_POLICY_BLOCK_FAIL_OPEN ->
                         PackageInstallerSession.this::resumeVerify;
                 case VERIFICATION_POLICY_BLOCK_FAIL_WARN -> onFailWarning;
                 case VERIFICATION_POLICY_BLOCK_FAIL_CLOSED -> onFailClosed;
                 default -> {
-                    Log.wtf(TAG, "Unknown verification policy: " + mVerificationPolicy.get());
+                    Log.wtf(TAG, "Unknown verification policy: "
+                            + mCurrentVerificationPolicy.get());
                     yield onFailClosed;
                 }
             };
@@ -4447,9 +4454,7 @@
      * @return the uid of the owner this session
      */
     public int getInstallerUid() {
-        synchronized (mLock) {
-            return mInstallerUid;
-        }
+        return mInstallerUid;
     }
 
     /**
@@ -5440,12 +5445,21 @@
     }
 
     /**
+     * @return the initial policy for the verification request assigned to the session when created.
+     */
+    @VisibleForTesting
+    public @PackageInstaller.VerificationPolicy int getInitialVerificationPolicy() {
+        assertCallerIsOwnerOrRoot();
+        return mInitialVerificationPolicy;
+    }
+
+    /**
      * @return the current policy for the verification request associated with this session.
      */
     @VisibleForTesting
-    public @PackageInstaller.VerificationPolicy int getVerificationPolicy() {
+    public @PackageInstaller.VerificationPolicy int getCurrentVerificationPolicy() {
         assertCallerIsOwnerOrRoot();
-        return mVerificationPolicy.get();
+        return mCurrentVerificationPolicy.get();
     }
 
     void setSessionReady() {
@@ -5685,6 +5699,8 @@
         if (mPreVerifiedDomains != null) {
             pw.printPair("mPreVerifiedDomains", mPreVerifiedDomains);
         }
+        pw.printPair("mInitialVerificationPolicy", mInitialVerificationPolicy);
+        pw.printPair("mCurrentVerificationPolicy", mCurrentVerificationPolicy.get());
         pw.println();
 
         pw.decreaseIndent();
@@ -5909,7 +5925,9 @@
             out.attributeInt(null, ATTR_INSTALL_REASON, params.installReason);
             writeBooleanAttribute(out, ATTR_APPLICATION_ENABLED_SETTING_PERSISTENT,
                     params.applicationEnabledSettingPersistent);
-            out.attributeInt(null, ATTR_VERIFICATION_POLICY, mVerificationPolicy.get());
+            out.attributeInt(null, ATTR_INITIAL_VERIFICATION_POLICY, mInitialVerificationPolicy);
+            out.attributeInt(null, ATTR_CURRENT_VERIFICATION_POLICY,
+                    mCurrentVerificationPolicy.get());
 
             final boolean isDataLoader = params.dataLoaderParams != null;
             writeBooleanAttribute(out, ATTR_IS_DATALOADER, isDataLoader);
@@ -6060,8 +6078,10 @@
         final boolean sealed = in.getAttributeBoolean(null, ATTR_SEALED, false);
         final int parentSessionId = in.getAttributeInt(null, ATTR_PARENT_SESSION_ID,
                 SessionInfo.INVALID_ID);
-        final int verificationPolicy = in.getAttributeInt(null, ATTR_VERIFICATION_POLICY,
-                VERIFICATION_POLICY_NONE);
+        final int initialVerificationPolicy = in.getAttributeInt(null,
+                ATTR_INITIAL_VERIFICATION_POLICY, VERIFICATION_POLICY_NONE);
+        final int currentVerificationPolicy = in.getAttributeInt(null,
+                ATTR_CURRENT_VERIFICATION_POLICY, VERIFICATION_POLICY_NONE);
 
         final SessionParams params = new SessionParams(
                 SessionParams.MODE_INVALID);
@@ -6237,6 +6257,6 @@
                 stageCid, fileArray, checksumsMap, prepared, committed, destroyed, sealed,
                 childSessionIdsArray, parentSessionId, isReady, isFailed, isApplied,
                 sessionErrorCode, sessionErrorMessage, preVerifiedDomains, verifierController,
-                verificationPolicy);
+                initialVerificationPolicy, currentVerificationPolicy);
     }
 }
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index d78f122..9d48efe9 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -4310,6 +4310,10 @@
                 if (intent == null) {
                     return;
                 }
+                final String action = intent.getAction();
+                if (action == null) {
+                    return;
+                }
                 Uri data = intent.getData();
                 if (data == null) {
                     return;
@@ -4417,6 +4421,7 @@
             mPendingBroadcasts.remove(userId);
             mAppsFilter.onUserDeleted(snapshotComputer(), userId);
             mPermissionManager.onUserRemoved(userId);
+            mInstallerService.onUserRemoved(userId);
         }
         mInstantAppRegistry.onUserRemoved(userId);
         mPackageMonitorCallbackHelper.onUserRemoved(userId);
@@ -4467,6 +4472,7 @@
             mLegacyPermissionManager.grantDefaultPermissions(userId);
             mPermissionManager.setDefaultPermissionGrantFingerprint(Build.FINGERPRINT, userId);
             mDomainVerificationManager.clearUser(userId);
+            mInstallerService.onUserAdded(userId);
         }
     }
 
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 4652c3a..7ef3582 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -399,6 +399,10 @@
                     return runUnarchive();
                 case "get-domain-verification-agent":
                     return runGetDomainVerificationAgent();
+                case "get-verification-policy":
+                    return runGetVerificationPolicy();
+                case "set-verification-policy":
+                    return runSetVerificationPolicy();
                 default: {
                     if (ART_SERVICE_COMMANDS.contains(cmd)) {
                         return runArtServiceCommand();
@@ -4645,6 +4649,87 @@
         return 0;
     }
 
+    private int runGetVerificationPolicy() throws RemoteException {
+        final PrintWriter pw = getOutPrintWriter();
+        int userId = UserHandle.USER_ALL;
+
+        String opt;
+        while ((opt = getNextOption()) != null) {
+            if (opt.equals("--user")) {
+                userId = UserHandle.parseUserArg(getNextArgRequired());
+                if (userId != UserHandle.USER_ALL && userId != UserHandle.USER_CURRENT) {
+                    UserManagerInternal umi =
+                            LocalServices.getService(UserManagerInternal.class);
+                    UserInfo userInfo = umi.getUserInfo(userId);
+                    if (userInfo == null) {
+                        pw.println("Failure [user " + userId + " doesn't exist]");
+                        return 1;
+                    }
+                }
+            } else {
+                pw.println("Error: Unknown option: " + opt);
+                return 1;
+            }
+        }
+        final int translatedUserId =
+                translateUserId(userId, UserHandle.USER_SYSTEM, "runGetVerificationPolicy");
+        try {
+            final IPackageInstaller installer = mInterface.getPackageInstaller();
+            // TODO(b/360129657): global verification policy should be per user
+            final int policy = installer.getVerificationPolicy(translatedUserId);
+            pw.println(policy);
+        } catch (Exception e) {
+            pw.println("Failure [" + e.getMessage() + "]");
+            return 1;
+        }
+        return 0;
+    }
+
+    private int runSetVerificationPolicy() throws RemoteException {
+        final PrintWriter pw = getOutPrintWriter();
+        int userId = UserHandle.USER_ALL;
+
+        String opt;
+        while ((opt = getNextOption()) != null) {
+            if (opt.equals("--user")) {
+                userId = UserHandle.parseUserArg(getNextArgRequired());
+                if (userId != UserHandle.USER_ALL && userId != UserHandle.USER_CURRENT) {
+                    UserManagerInternal umi =
+                            LocalServices.getService(UserManagerInternal.class);
+                    UserInfo userInfo = umi.getUserInfo(userId);
+                    if (userInfo == null) {
+                        pw.println("Failure [user " + userId + " doesn't exist]");
+                        return 1;
+                    }
+                }
+            } else {
+                pw.println("Error: Unknown option: " + opt);
+                return 1;
+            }
+        }
+        final String policyStr = getNextArg();
+        if (policyStr == null) {
+            pw.println("Error: policy not specified");
+            return 1;
+        }
+        final int translatedUserId =
+                translateUserId(userId, UserHandle.USER_SYSTEM, "runSetVerificationPolicy");
+        try {
+            final IPackageInstaller installer = mInterface.getPackageInstaller();
+            // TODO(b/360129657): global verification policy should be per user
+            final boolean success = installer.setVerificationPolicy(Integer.parseInt(policyStr),
+                    translatedUserId);
+            if (!success) {
+                pw.println("Failure setting verification policy.");
+                return 1;
+            }
+        } catch (Exception e) {
+            pw.println("Failure [" + e.getMessage() + "]");
+            return 1;
+        }
+        return 0;
+    }
+
     @Override
     public void onHelp() {
         final PrintWriter pw = getOutPrintWriter();
@@ -5073,6 +5158,14 @@
         pw.println("      --user: return the agent of the given user (SYSTEM_USER if unspecified)");
         pw.println("  get-package-storage-stats [--user <USER_ID>] <PACKAGE>");
         pw.println("    Return the storage stats for the given app, if present");
+        pw.println("  get-verification-policy [--user USER_ID]");
+        pw.println("    Display current verification enforcement policy which will be applied to");
+        pw.println("    all the future installation sessions");
+        pw.println("      --user: show the policy of the given user (SYSTEM_USER if unspecified)");
+        pw.println("  set-verification-policy POLICY [--user USER_ID]");
+        pw.println("    Sets the verification policy of all the future installation sessions.");
+        pw.println("      --user: set the policy of the given user (SYSTEM_USER if unspecified)");
+        pw.println("");
         pw.println("");
         printArtServiceHelp();
         pw.println("");
diff --git a/services/core/java/com/android/server/pm/verify/pkg/VerifierController.java b/services/core/java/com/android/server/pm/verify/pkg/VerifierController.java
index a35618b..78849d2 100644
--- a/services/core/java/com/android/server/pm/verify/pkg/VerifierController.java
+++ b/services/core/java/com/android/server/pm/verify/pkg/VerifierController.java
@@ -18,13 +18,12 @@
 
 import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
 import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
+import static android.os.Process.INVALID_UID;
 import static android.os.Process.SYSTEM_UID;
 import static android.provider.DeviceConfig.NAMESPACE_PACKAGE_MANAGER_SERVICE;
 
-import android.Manifest;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.RequiresPermission;
 import android.annotation.SuppressLint;
 import android.content.ComponentName;
 import android.content.Context;
@@ -114,10 +113,17 @@
 
     private final Context mContext;
     private final Handler mHandler;
+    // Guards the remote service object, as well as the verifier name and UID, which should all be
+    // changed at the same time.
+    private final Object mLock = new Object();
     @Nullable
+    @GuardedBy("mLock")
     private ServiceConnector<IVerifierService> mRemoteService;
     @Nullable
+    @GuardedBy("mLock")
     private ComponentName mRemoteServiceComponentName;
+    @GuardedBy("mLock")
+    private int mRemoteServiceUid = INVALID_UID;
     @NonNull
     private Injector mInjector;
 
@@ -143,9 +149,11 @@
      */
     @Nullable
     public String getVerifierPackageName(Supplier<Computer> snapshotSupplier, int userId) {
-        if (isVerifierConnected()) {
-            // Verifier is connected or is being connected, so it must be installed.
-            return mRemoteServiceComponentName.getPackageName();
+        synchronized (mLock) {
+            if (isVerifierConnectedLocked()) {
+                // Verifier is connected or is being connected, so it must be installed.
+                return mRemoteServiceComponentName.getPackageName();
+            }
         }
         // Verifier has been disconnected, or it hasn't been connected. Check if it's installed.
         return mInjector.getVerifierPackageName(snapshotSupplier.get(), userId);
@@ -178,16 +186,29 @@
             }
             return true;
         }
+        Computer snapshot = snapshotSupplier.get();
         Pair<ServiceConnector<IVerifierService>, ComponentName> result =
-                mInjector.getRemoteService(snapshotSupplier.get(), mContext, userId, mHandler);
+                mInjector.getRemoteService(snapshot, mContext, userId, mHandler);
         if (result == null || result.first == null) {
             if (DEBUG) {
                 Slog.i(TAG, "Unable to find a qualified verifier.");
             }
             return false;
         }
-        mRemoteService = result.first;
-        mRemoteServiceComponentName = result.second;
+        final int verifierUid = snapshot.getPackageUidInternal(
+                result.second.getPackageName(), 0, userId, /* callingUid= */ SYSTEM_UID);
+        if (verifierUid == INVALID_UID) {
+            if (DEBUG) {
+                Slog.i(TAG, "Unable to find the UID of the qualified verifier.");
+            }
+            return false;
+        }
+        synchronized (mLock) {
+            mRemoteService = result.first;
+            mRemoteServiceComponentName = result.second;
+            mRemoteServiceUid = verifierUid;
+        }
+
         if (DEBUG) {
             Slog.i(TAG, "Connecting to a qualified verifier: " + mRemoteServiceComponentName);
         }
@@ -212,10 +233,13 @@
                     }
 
                     private void destroy() {
-                        if (isVerifierConnected()) {
-                            mRemoteService.unbind();
-                            mRemoteService = null;
-                            mRemoteServiceComponentName = null;
+                        synchronized (mLock) {
+                            if (isVerifierConnectedLocked()) {
+                                mRemoteService.unbind();
+                                mRemoteService = null;
+                                mRemoteServiceComponentName = null;
+                                mRemoteServiceUid = INVALID_UID;
+                            }
                         }
                     }
                 });
@@ -223,7 +247,8 @@
         return true;
     }
 
-    private boolean isVerifierConnected() {
+    @GuardedBy("mLock")
+    private boolean isVerifierConnectedLocked() {
         return mRemoteService != null && mRemoteServiceComponentName != null;
     }
 
@@ -232,19 +257,21 @@
      * requested for verification.
      */
     public void notifyPackageNameAvailable(@NonNull String packageName) {
-        if (!isVerifierConnected()) {
-            if (DEBUG) {
-                Slog.i(TAG, "Verifier is not connected. Not notifying package name available");
+        synchronized (mLock) {
+            if (!isVerifierConnectedLocked()) {
+                if (DEBUG) {
+                    Slog.i(TAG, "Verifier is not connected. Not notifying package name available");
+                }
+                return;
             }
-            return;
+            // Best effort. We don't check for the result.
+            mRemoteService.run(service -> {
+                if (DEBUG) {
+                    Slog.i(TAG, "Notifying package name available for " + packageName);
+                }
+                service.onPackageNameAvailable(packageName);
+            });
         }
-        // Best effort. We don't check for the result.
-        mRemoteService.run(service -> {
-            if (DEBUG) {
-                Slog.i(TAG, "Notifying package name available for " + packageName);
-            }
-            service.onPackageNameAvailable(packageName);
-        });
     }
 
     /**
@@ -253,27 +280,29 @@
      * will no longer be requested for verification, possibly because the installation is canceled.
      */
     public void notifyVerificationCancelled(@NonNull String packageName) {
-        if (!isVerifierConnected()) {
-            if (DEBUG) {
-                Slog.i(TAG, "Verifier is not connected. Not notifying verification cancelled");
+        synchronized (mLock) {
+            if (!isVerifierConnectedLocked()) {
+                if (DEBUG) {
+                    Slog.i(TAG, "Verifier is not connected. Not notifying verification cancelled");
+                }
+                return;
             }
-            return;
+            // Best effort. We don't check for the result.
+            mRemoteService.run(service -> {
+                if (DEBUG) {
+                    Slog.i(TAG, "Notifying verification cancelled for " + packageName);
+                }
+                service.onVerificationCancelled(packageName);
+            });
         }
-        // Best effort. We don't check for the result.
-        mRemoteService.run(service -> {
-            if (DEBUG) {
-                Slog.i(TAG, "Notifying verification cancelled for " + packageName);
-            }
-            service.onVerificationCancelled(packageName);
-        });
     }
 
     /**
      * Called to notify the bound verifier agent that a package that's pending installation needs
      * to be verified right now.
      * <p>The verification request must be sent to the verifier as soon as the verifier is
-     * connected. If the connection cannot be made within {@link #CONNECTION_TIMEOUT_SECONDS}</p>
-     * of when the request is sent out, we consider the verification to be failed and notify the
+     * connected. If the connection cannot be made within the specified time limit from
+     * when the request is sent out, we consider the verification to be failed and notify the
      * installation session.</p>
      * <p>If a response is not returned from the verifier agent within a timeout duration from the
      * time the request is sent to the verifier, the verification will be considered a failure.</p>
@@ -291,43 +320,48 @@
         if (!bindToVerifierServiceIfNeeded(snapshotSupplier, userId)) {
             return false;
         }
-        if (!isVerifierConnected()) {
-            if (DEBUG) {
-                Slog.i(TAG, "Verifier is not connected. Not notifying verification required");
-            }
-            // Normally this should not happen because we just tried to bind. But if the verifier
-            // just crashed or just became unavailable, we should notify the installation session so
-            // it can finish with a verification failure.
-            return false;
-        }
         // For now, the verification id is the same as the installation session id.
         final int verificationId = installationSessionId;
-        final VerificationSession session = new VerificationSession(
-                /* id= */ verificationId,
-                /* installSessionId= */ installationSessionId,
-                packageName, stagedPackageUri, signingInfo, declaredLibraries, extensionParams,
-                verificationPolicy, new VerificationSessionInterface(callback));
-        AndroidFuture<Void> unusedFuture = mRemoteService.post(service -> {
-            if (!retry) {
+        synchronized (mLock) {
+            if (!isVerifierConnectedLocked()) {
                 if (DEBUG) {
-                    Slog.i(TAG, "Notifying verification required for session " + verificationId);
+                    Slog.i(TAG, "Verifier is not connected. Not notifying verification required");
                 }
-                service.onVerificationRequired(session);
-            } else {
-                if (DEBUG) {
-                    Slog.i(TAG, "Notifying verification retry for session " + verificationId);
+                // Normally this should not happen because we just tried to bind. But if the
+                // verifier just crashed or just became unavailable, we should notify the
+                // installation session so it can finish with a verification failure.
+                return false;
+            }
+            final VerificationSession session = new VerificationSession(
+                    /* id= */ verificationId,
+                    /* installSessionId= */ installationSessionId,
+                    packageName, stagedPackageUri, signingInfo, declaredLibraries, extensionParams,
+                    verificationPolicy, new VerificationSessionInterface(callback));
+            AndroidFuture<Void> unusedFuture = mRemoteService.post(service -> {
+                if (!retry) {
+                    if (DEBUG) {
+                        Slog.i(TAG, "Notifying verification required for session "
+                                + verificationId);
+                    }
+                    service.onVerificationRequired(session);
+                } else {
+                    if (DEBUG) {
+                        Slog.i(TAG, "Notifying verification retry for session "
+                                + verificationId);
+                    }
+                    service.onVerificationRetry(session);
                 }
-                service.onVerificationRetry(session);
-            }
-        }).orTimeout(mInjector.getVerifierConnectionTimeoutMillis(), TimeUnit.MILLISECONDS)
-                .whenComplete((res, err) -> {
-            if (err != null) {
-                Slog.e(TAG, "Error notifying verification request for session " + verificationId,
-                        err);
-                // Notify the installation session so it can finish with verification failure.
-                callback.onConnectionFailed();
-            }
-        });
+            }).orTimeout(mInjector.getVerifierConnectionTimeoutMillis(), TimeUnit.MILLISECONDS)
+                    .whenComplete((res, err) -> {
+                        if (err != null) {
+                            Slog.e(TAG, "Error notifying verification request for session "
+                                    + verificationId, err);
+                            // Notify the installation session so it can finish with verification
+                            // failure.
+                            callback.onConnectionFailed();
+                        }
+                    });
+        }
         // Keep track of the session status with the ID. Start counting down the session timeout.
         final long defaultTimeoutMillis = mInjector.getVerificationRequestTimeoutMillis();
         final long maxExtendedTimeoutMillis = mInjector.getMaxVerificationExtendedTimeoutMillis();
@@ -369,24 +403,27 @@
      * Called to notify the bound verifier agent that a verification request has timed out.
      */
     public void notifyVerificationTimeout(int verificationId) {
-        if (!isVerifierConnected()) {
-            if (DEBUG) {
-                Slog.i(TAG,
-                        "Verifier is not connected. Not notifying timeout for " + verificationId);
+        synchronized (mLock) {
+            if (!isVerifierConnectedLocked()) {
+                if (DEBUG) {
+                    Slog.i(TAG,
+                            "Verifier is not connected. Not notifying timeout for "
+                                    + verificationId);
+                }
+                return;
             }
-            return;
+            AndroidFuture<Void> unusedFuture = mRemoteService.post(service -> {
+                if (DEBUG) {
+                    Slog.i(TAG, "Notifying timeout for " + verificationId);
+                }
+                service.onVerificationTimeout(verificationId);
+            }).whenComplete((res, err) -> {
+                if (err != null) {
+                    Slog.e(TAG, "Error notifying VerificationTimeout for session "
+                            + verificationId, err);
+                }
+            });
         }
-        AndroidFuture<Void> unusedFuture = mRemoteService.post(service -> {
-            if (DEBUG) {
-                Slog.i(TAG, "Notifying timeout for " + verificationId);
-            }
-            service.onVerificationTimeout(verificationId);
-        }).whenComplete((res, err) -> {
-            if (err != null) {
-                Slog.e(TAG, "Error notifying VerificationTimeout for session "
-                        + verificationId, (Throwable) err);
-            }
-        });
     }
 
     /**
@@ -405,17 +442,19 @@
         }
     }
 
-    @RequiresPermission(Manifest.permission.VERIFICATION_AGENT)
-    private void checkCallerPermission() {
-        // TODO: think of a better way to test it on non-eng builds
-        if (Build.IS_ENG) {
-            return;
-        }
-        if (mContext.checkCallingOrSelfPermission(Manifest.permission.VERIFICATION_AGENT)
-                != PackageManager.PERMISSION_GRANTED) {
-            throw new SecurityException("You need the"
-                    + " com.android.permission.VERIFICATION_AGENT permission"
-                    + " to use VerificationSession APIs.");
+    /**
+     * Assert that the calling UID is the same as the UID of the currently connected verifier.
+     */
+    public void assertCallerIsCurrentVerifier(int callingUid) {
+        synchronized (mLock) {
+            if (!isVerifierConnectedLocked()) {
+                throw new IllegalStateException(
+                        "Unable to proceed because the verifier has been disconnected.");
+            }
+            if (callingUid != mRemoteServiceUid) {
+                throw new IllegalStateException(
+                        "Calling uid " + callingUid + " is not the current verifier.");
+            }
         }
     }
 
@@ -429,7 +468,7 @@
 
         @Override
         public long getTimeoutTime(int verificationId) {
-            checkCallerPermission();
+            assertCallerIsCurrentVerifier(getCallingUid());
             synchronized (mVerificationStatus) {
                 final VerificationStatusTracker tracker = mVerificationStatus.get(verificationId);
                 if (tracker == null) {
@@ -442,7 +481,7 @@
 
         @Override
         public long extendTimeRemaining(int verificationId, long additionalMs) {
-            checkCallerPermission();
+            assertCallerIsCurrentVerifier(getCallingUid());
             synchronized (mVerificationStatus) {
                 final VerificationStatusTracker tracker = mVerificationStatus.get(verificationId);
                 if (tracker == null) {
@@ -456,7 +495,7 @@
         @Override
         public boolean setVerificationPolicy(int verificationId,
                 @PackageInstaller.VerificationPolicy int policy) {
-            checkCallerPermission();
+            assertCallerIsCurrentVerifier(getCallingUid());
             synchronized (mVerificationStatus) {
                 final VerificationStatusTracker tracker = mVerificationStatus.get(verificationId);
                 if (tracker == null) {
@@ -469,7 +508,7 @@
 
         @Override
         public void reportVerificationIncomplete(int id, int reason) {
-            checkCallerPermission();
+            assertCallerIsCurrentVerifier(getCallingUid());
             final VerificationStatusTracker tracker;
             synchronized (mVerificationStatus) {
                 tracker = mVerificationStatus.get(id);
@@ -484,15 +523,9 @@
         }
 
         @Override
-        public void reportVerificationComplete(int id, VerificationStatus verificationStatus) {
-            reportVerificationCompleteWithExtensionResponse(id, verificationStatus,
-                    /* extensionResponse= */ null);
-        }
-
-        @Override
-        public void reportVerificationCompleteWithExtensionResponse(int id,
-                VerificationStatus verificationStatus, PersistableBundle extensionResponse) {
-            checkCallerPermission();
+        public void reportVerificationComplete(int id, VerificationStatus verificationStatus,
+                @Nullable PersistableBundle extensionResponse) {
+            assertCallerIsCurrentVerifier(getCallingUid());
             final VerificationStatusTracker tracker;
             synchronized (mVerificationStatus) {
                 tracker = mVerificationStatus.get(id);
diff --git a/services/core/java/com/android/server/policy/ModifierShortcutManager.java b/services/core/java/com/android/server/policy/ModifierShortcutManager.java
index 66ec53e..a1236e5 100644
--- a/services/core/java/com/android/server/policy/ModifierShortcutManager.java
+++ b/services/core/java/com/android/server/policy/ModifierShortcutManager.java
@@ -31,6 +31,7 @@
 import android.content.pm.PackageManager;
 import android.content.res.XmlResourceParser;
 import android.graphics.drawable.Icon;
+import android.hardware.input.AppLaunchData;
 import android.hardware.input.KeyGestureEvent;
 import android.os.Handler;
 import android.os.RemoteException;
@@ -48,6 +49,7 @@
 import android.view.KeyboardShortcutInfo;
 
 import com.android.internal.R;
+import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.policy.IShortcutService;
 import com.android.internal.util.XmlUtils;
@@ -63,6 +65,7 @@
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Set;
 
 /**
@@ -131,7 +134,10 @@
     private boolean mConsumeSearchKeyUp = true;
     private UserHandle mCurrentUser;
     private final Map<Pair<Character, Boolean>, Bookmark> mBookmarks = new HashMap<>();
+    @GuardedBy("mAppIntentCache")
+    private final Map<AppLaunchData, Intent> mAppIntentCache = new HashMap<>();
 
+    @SuppressLint("MissingPermission")
     ModifierShortcutManager(Context context, Handler handler, UserHandle currentUser) {
         mContext = context;
         mHandler = handler;
@@ -146,6 +152,17 @@
                     } else {
                         mRoleIntents.remove(roleName);
                     }
+                    synchronized (mAppIntentCache) {
+                        mAppIntentCache.entrySet().removeIf(
+                                entry -> {
+                                    if (entry.getKey() instanceof AppLaunchData.RoleData) {
+                                        return Objects.equals(
+                                                ((AppLaunchData.RoleData) entry.getKey()).getRole(),
+                                                roleName);
+                                    }
+                                    return false;
+                                });
+                    }
                 }, UserHandle.ALL);
         mCurrentUser = currentUser;
         mInputManagerInternal = LocalServices.getService(InputManagerInternal.class);
@@ -159,6 +176,10 @@
         // so clear the cache.
         clearRoleIntents();
         clearComponentIntents();
+
+        synchronized (mAppIntentCache) {
+            mAppIntentCache.clear();
+        }
     }
 
     void clearRoleIntents() {
@@ -748,6 +769,46 @@
                 shortcuts);
     }
 
+    private Intent getIntentFromAppLaunchData(@NonNull AppLaunchData data) {
+        Context context = mContext.createContextAsUser(mCurrentUser, 0);
+        synchronized (mAppIntentCache) {
+            Intent intent = mAppIntentCache.get(data);
+            if (intent != null) {
+                return intent;
+            }
+            if (data instanceof AppLaunchData.CategoryData) {
+                intent = Intent.makeMainSelectorActivity(Intent.ACTION_MAIN,
+                        ((AppLaunchData.CategoryData) data).getCategory());
+            } else if (data instanceof AppLaunchData.RoleData) {
+                intent = getRoleLaunchIntent(context, ((AppLaunchData.RoleData) data).getRole());
+            } else if (data instanceof AppLaunchData.ComponentData) {
+                AppLaunchData.ComponentData componentData = (AppLaunchData.ComponentData) data;
+                intent = resolveComponentNameIntent(context, componentData.getPackageName(),
+                        componentData.getClassName());
+            }
+            if (intent != null) {
+                mAppIntentCache.put(data, intent);
+            }
+            return intent;
+        }
+    }
+
+    boolean launchApplication(@NonNull AppLaunchData data) {
+        Intent intent  = getIntentFromAppLaunchData(data);
+        if (intent == null) {
+            return false;
+        }
+        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        try {
+            mContext.startActivityAsUser(intent, mCurrentUser);
+            return true;
+        } catch (ActivityNotFoundException ex) {
+            Slog.w(TAG, "Not launching app because "
+                    + "the activity to which it refers to was not found: " + data);
+        }
+        return false;
+    }
+
     /**
      * Given an intent to launch an application and the character and shift state that should
      * trigger it, return a suitable {@link KeyboardShortcutInfo} that contains the label and
@@ -869,7 +930,7 @@
 
         // TODO(b/280423320): Add new field package name associated in the
         //  KeyboardShortcutEvent atom and log it accordingly.
-        return KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION_BY_PACKAGE_NAME;
+        return KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION;
     }
 
     @KeyGestureEvent.KeyGestureType
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 6a7f22e..2893430 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -149,7 +149,9 @@
 import android.hardware.hdmi.HdmiControlManager;
 import android.hardware.hdmi.HdmiPlaybackClient;
 import android.hardware.hdmi.HdmiPlaybackClient.OneTouchPlayCallback;
+import android.hardware.input.AppLaunchData;
 import android.hardware.input.InputManager;
+import android.hardware.input.InputSettings;
 import android.hardware.input.KeyGestureEvent;
 import android.media.AudioManager;
 import android.media.AudioManagerInternal;
@@ -3616,6 +3618,68 @@
                     }
                 }
                 break;
+            case KeyEvent.KEYCODE_3:
+                if (InputSettings.isAccessibilityBounceKeysFeatureEnabled()
+                        && keyboardA11yShortcutControl()) {
+                    if (firstDown && event.isMetaPressed()
+                            && event.isAltPressed()) {
+                        final boolean bounceKeysEnabled =
+                                InputSettings.isAccessibilityBounceKeysEnabled(
+                                        mContext);
+                        InputSettings.setAccessibilityBounceKeysThreshold(mContext,
+                                bounceKeysEnabled ? 0
+                                        : InputSettings.DEFAULT_BOUNCE_KEYS_THRESHOLD_MILLIS);
+                        notifyKeyGestureCompleted(event,
+                                KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_BOUNCE_KEYS);
+                        return true;
+                    }
+                }
+                break;
+            case KeyEvent.KEYCODE_4:
+                if (InputSettings.isAccessibilityMouseKeysFeatureFlagEnabled()
+                        && keyboardA11yShortcutControl()) {
+                    if (firstDown && event.isMetaPressed() && event.isAltPressed()) {
+                        final boolean mouseKeysEnabled =
+                                InputSettings.isAccessibilityMouseKeysEnabled(
+                                        mContext);
+                        InputSettings.setAccessibilityMouseKeysEnabled(mContext,
+                                !mouseKeysEnabled);
+                        notifyKeyGestureCompleted(event,
+                                KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_MOUSE_KEYS);
+                        return true;
+                    }
+                }
+                break;
+            case KeyEvent.KEYCODE_5:
+                if (InputSettings.isAccessibilityStickyKeysFeatureEnabled()
+                        && keyboardA11yShortcutControl()) {
+                    if (firstDown && event.isMetaPressed() && event.isAltPressed()) {
+                        final boolean stickyKeysEnabled =
+                                InputSettings.isAccessibilityStickyKeysEnabled(
+                                        mContext);
+                        InputSettings.setAccessibilityStickyKeysEnabled(mContext,
+                                !stickyKeysEnabled);
+                        notifyKeyGestureCompleted(event,
+                                KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_STICKY_KEYS);
+                        return true;
+                    }
+                }
+                break;
+            case KeyEvent.KEYCODE_6:
+                if (InputSettings.isAccessibilitySlowKeysFeatureFlagEnabled()
+                        && keyboardA11yShortcutControl()) {
+                    if (firstDown && event.isMetaPressed() && event.isAltPressed()) {
+                        final boolean slowKeysEnabled =
+                                InputSettings.isAccessibilitySlowKeysEnabled(mContext);
+                        InputSettings.setAccessibilitySlowKeysThreshold(mContext,
+                                slowKeysEnabled ? 0
+                                        : InputSettings.DEFAULT_SLOW_KEYS_THRESHOLD_MILLIS);
+                        notifyKeyGestureCompleted(event,
+                                KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_SLOW_KEYS);
+                        return true;
+                    }
+                }
+                break;
             case KeyEvent.KEYCODE_DEL:
                 if (newBugreportKeyboardShortcut()) {
                     if (mEnableBugReportKeyboardShortcut && firstDown
@@ -4037,6 +4101,7 @@
                     case KeyGestureEvent.KEY_GESTURE_TYPE_LANGUAGE_SWITCH:
                     case KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_SHORTCUT:
                     case KeyGestureEvent.KEY_GESTURE_TYPE_CLOSE_ALL_DIALOGS:
+                    case KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION:
                         return true;
                     case KeyGestureEvent.KEY_GESTURE_TYPE_SCREENSHOT_CHORD:
                     case KeyGestureEvent.KEY_GESTURE_TYPE_RINGER_TOGGLE_CHORD:
@@ -4051,6 +4116,18 @@
                                 .isAccessibilityShortcutAvailable(false);
                     case KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_TALKBACK:
                         return keyboardA11yShortcutControl();
+                    case KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_SLOW_KEYS:
+                        return InputSettings.isAccessibilitySlowKeysFeatureFlagEnabled()
+                                && keyboardA11yShortcutControl();
+                    case KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_BOUNCE_KEYS:
+                        return InputSettings.isAccessibilityBounceKeysFeatureEnabled()
+                                && keyboardA11yShortcutControl();
+                    case KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_MOUSE_KEYS:
+                        return InputSettings.isAccessibilityMouseKeysFeatureFlagEnabled()
+                                && keyboardA11yShortcutControl();
+                    case KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_STICKY_KEYS:
+                        return InputSettings.isAccessibilityStickyKeysFeatureEnabled()
+                                && keyboardA11yShortcutControl();
                     default:
                         return false;
                 }
@@ -4279,6 +4356,65 @@
                     return true;
                 }
                 break;
+            case KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION:
+                AppLaunchData data = event.getAppLaunchData();
+                if (complete && isUserSetupComplete() && !keyguardOn
+                        && data != null && mModifierShortcutManager.launchApplication(data)) {
+                    dismissKeyboardShortcutsMenu();
+                }
+                return true;
+            case KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_BOUNCE_KEYS:
+                if (InputSettings.isAccessibilityBounceKeysFeatureEnabled()
+                        && keyboardA11yShortcutControl()) {
+                    if (complete) {
+                        final boolean bounceKeysEnabled =
+                                InputSettings.isAccessibilityBounceKeysEnabled(
+                                        mContext);
+                        InputSettings.setAccessibilityBounceKeysThreshold(mContext,
+                                bounceKeysEnabled ? 0
+                                        : InputSettings.DEFAULT_BOUNCE_KEYS_THRESHOLD_MILLIS);
+                    }
+                    return true;
+                }
+                break;
+            case KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_MOUSE_KEYS:
+                if (InputSettings.isAccessibilityMouseKeysFeatureFlagEnabled()
+                        && keyboardA11yShortcutControl()) {
+                    if (complete) {
+                        final boolean mouseKeysEnabled =
+                                InputSettings.isAccessibilityMouseKeysEnabled(
+                                        mContext);
+                        InputSettings.setAccessibilityMouseKeysEnabled(mContext,
+                                !mouseKeysEnabled);
+                    }
+                    return true;
+                }
+                break;
+            case KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_STICKY_KEYS:
+                if (InputSettings.isAccessibilityStickyKeysFeatureEnabled()
+                        && keyboardA11yShortcutControl()) {
+                    if (complete) {
+                        final boolean stickyKeysEnabled =
+                                InputSettings.isAccessibilityStickyKeysEnabled(mContext);
+                        InputSettings.setAccessibilityStickyKeysEnabled(mContext,
+                                !stickyKeysEnabled);
+                    }
+                    return true;
+                }
+                break;
+            case KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_SLOW_KEYS:
+                if (InputSettings.isAccessibilitySlowKeysFeatureFlagEnabled()
+                        && keyboardA11yShortcutControl()) {
+                    if (complete) {
+                        final boolean slowKeysEnabled =
+                                InputSettings.isAccessibilitySlowKeysEnabled(mContext);
+                        InputSettings.setAccessibilitySlowKeysThreshold(mContext,
+                                slowKeysEnabled ? 0
+                                        : InputSettings.DEFAULT_SLOW_KEYS_THRESHOLD_MILLIS);
+                    }
+                    return true;
+                }
+                break;
         }
         return false;
     }
@@ -6948,6 +7084,7 @@
         if (modifierShortcutManagerMultiuser()) {
             mModifierShortcutManager.setCurrentUser(UserHandle.of(newUserId));
         }
+        mInputManagerInternal.setCurrentUser(newUserId);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/security/advancedprotection/AdvancedProtectionService.java b/services/core/java/com/android/server/security/advancedprotection/AdvancedProtectionService.java
index 9a63c823..c33ed55 100644
--- a/services/core/java/com/android/server/security/advancedprotection/AdvancedProtectionService.java
+++ b/services/core/java/com/android/server/security/advancedprotection/AdvancedProtectionService.java
@@ -21,6 +21,7 @@
 import android.Manifest;
 import android.annotation.EnforcePermission;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.content.Context;
 import android.os.Binder;
 import android.os.Handler;
@@ -35,37 +36,55 @@
 import android.security.advancedprotection.IAdvancedProtectionCallback;
 import android.security.advancedprotection.IAdvancedProtectionService;
 import android.util.ArrayMap;
+import android.util.Slog;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.FgThread;
 import com.android.server.LocalServices;
 import com.android.server.SystemService;
 import com.android.server.pm.UserManagerInternal;
+import com.android.server.security.advancedprotection.features.AdvancedProtectionHook;
 
 import java.io.FileDescriptor;
 import java.util.ArrayList;
 
 /** @hide */
 public class AdvancedProtectionService extends IAdvancedProtectionService.Stub  {
+    private static final String TAG = "AdvancedProtectionService";
     private static final int MODE_CHANGED = 0;
     private static final int CALLBACK_ADDED = 1;
 
+    private final Context mContext;
     private final Handler mHandler;
     private final AdvancedProtectionStore mStore;
+
+    // Features owned by the service - their code will be executed when state changes
+    private final ArrayList<AdvancedProtectionHook> mHooks = new ArrayList<>();
+    // External features - they will be called on state change
     private final ArrayMap<IBinder, IAdvancedProtectionCallback> mCallbacks = new ArrayMap<>();
 
     private AdvancedProtectionService(@NonNull Context context) {
         super(PermissionEnforcer.fromContext(context));
+        mContext = context;
         mHandler = new AdvancedProtectionHandler(FgThread.get().getLooper());
         mStore = new AdvancedProtectionStore(context);
     }
 
+    private void initFeatures(boolean enabled) {
+    }
+
+    // Only for tests
     @VisibleForTesting
     AdvancedProtectionService(@NonNull Context context, @NonNull AdvancedProtectionStore store,
-            @NonNull Looper looper, @NonNull PermissionEnforcer permissionEnforcer) {
+            @NonNull Looper looper, @NonNull PermissionEnforcer permissionEnforcer,
+            @Nullable AdvancedProtectionHook hook) {
         super(permissionEnforcer);
+        mContext = context;
         mStore = store;
         mHandler = new AdvancedProtectionHandler(looper);
+        if (hook != null) {
+            mHooks.add(hook);
+        }
     }
 
     @Override
@@ -100,8 +119,8 @@
 
     @Override
     @EnforcePermission(Manifest.permission.QUERY_ADVANCED_PROTECTION_MODE)
-    public void unregisterAdvancedProtectionCallback(@NonNull IAdvancedProtectionCallback callback)
-            throws RemoteException {
+    public void unregisterAdvancedProtectionCallback(
+            @NonNull IAdvancedProtectionCallback callback) {
         unregisterAdvancedProtectionCallback_enforcePermission();
         synchronized (mCallbacks) {
             mCallbacks.remove(callback.asBinder());
@@ -118,6 +137,7 @@
                 if (enabled != isAdvancedProtectionEnabledInternal()) {
                     mStore.store(enabled);
                     sendModeChanged(enabled);
+                    Slog.i(TAG, "Advanced protection is " + (enabled ? "enabled" : "disabled"));
                 }
             }
         } finally {
@@ -156,6 +176,17 @@
         public void onStart() {
             publishBinderService(Context.ADVANCED_PROTECTION_SERVICE, mService);
         }
+
+        @Override
+        public void onBootPhase(@BootPhase int phase) {
+            if (phase == PHASE_SYSTEM_SERVICES_READY) {
+                boolean enabled = mService.isAdvancedProtectionEnabledInternal();
+                if (enabled) {
+                    Slog.i(TAG, "Advanced protection is enabled");
+                }
+                mService.initFeatures(enabled);
+            }
+        }
     }
 
     @VisibleForTesting
@@ -205,6 +236,12 @@
         private void handleAllCallbacks(boolean enabled) {
             ArrayList<IAdvancedProtectionCallback> deadObjects = new ArrayList<>();
 
+            for (int i = 0; i < mHooks.size(); i++) {
+                AdvancedProtectionHook feature = mHooks.get(i);
+                if (feature.isAvailable()) {
+                    feature.onAdvancedProtectionChanged(enabled);
+                }
+            }
             synchronized (mCallbacks) {
                 for (int i = 0; i < mCallbacks.size(); i++) {
                     IAdvancedProtectionCallback callback = mCallbacks.valueAt(i);
diff --git a/services/core/java/com/android/server/security/advancedprotection/features/AdvancedProtectionHook.java b/services/core/java/com/android/server/security/advancedprotection/features/AdvancedProtectionHook.java
new file mode 100644
index 0000000..b2acc51
--- /dev/null
+++ b/services/core/java/com/android/server/security/advancedprotection/features/AdvancedProtectionHook.java
@@ -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.server.security.advancedprotection.features;
+
+import android.annotation.NonNull;
+import android.content.Context;
+
+/** @hide */
+public abstract class AdvancedProtectionHook {
+    /** Called on boot phase PHASE_SYSTEM_SERVICES_READY */
+    public AdvancedProtectionHook(@NonNull Context context, boolean enabled) {}
+    /** Whether this feature is relevant on this device. If false, onAdvancedProtectionChanged will
+     * not be called, and the feature will not be displayed in the onboarding UX. */
+    public abstract boolean isAvailable();
+    /** Called whenever advanced protection state changes */
+    public void onAdvancedProtectionChanged(boolean enabled) {}
+}
diff --git a/services/core/java/com/android/server/security/forensic/DataAggregator.java b/services/core/java/com/android/server/security/forensic/DataAggregator.java
new file mode 100644
index 0000000..cc473ca
--- /dev/null
+++ b/services/core/java/com/android/server/security/forensic/DataAggregator.java
@@ -0,0 +1,151 @@
+/*
+ * 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.security.forensic;
+
+import android.content.Context;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.security.forensic.ForensicEvent;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.ServiceThread;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class DataAggregator {
+    private static final String TAG = "Forensic DataAggregator";
+    private static final int MSG_SINGLE_DATA = 0;
+    private static final int MSG_BATCH_DATA = 1;
+    private static final int MSG_DISABLE = 2;
+
+    private static final int STORED_EVENTS_SIZE_LIMIT = 1024;
+    private final ForensicService mForensicService;
+    private final ArrayList<DataSource> mDataSources;
+
+    private Context mContext;
+    private List<ForensicEvent> mStoredEvents = new ArrayList<>();
+    private ServiceThread mHandlerThread;
+    private Handler mHandler;
+
+    public DataAggregator(Context context, ForensicService forensicService) {
+        mForensicService = forensicService;
+        mContext = context;
+        mDataSources = new ArrayList<DataSource>();
+    }
+
+    @VisibleForTesting
+    void setHandler(Looper looper, ServiceThread serviceThread) {
+        mHandlerThread = serviceThread;
+        mHandler = new EventHandler(looper, this);
+    }
+
+    /**
+     * Initialize DataSources
+     * @return Whether the initialization succeeds.
+     */
+    // TODO: Add the corresponding data sources
+    public boolean initialize() {
+        SecurityLogSource securityLogSource = new SecurityLogSource(mContext, this);
+        mDataSources.add(securityLogSource);
+        return true;
+    }
+
+    /**
+     * Enable the data collection of all DataSources.
+     */
+    public void enable() {
+        mHandlerThread = new ServiceThread(TAG, android.os.Process.THREAD_PRIORITY_BACKGROUND,
+                /* allowIo */ false);
+        mHandlerThread.start();
+        mHandler = new EventHandler(mHandlerThread.getLooper(), this);
+        for (DataSource ds : mDataSources) {
+            ds.enable();
+        }
+    }
+
+    /**
+     * DataSource calls it to transmit a single event.
+     */
+    public void addSingleData(ForensicEvent event) {
+        mHandler.obtainMessage(MSG_SINGLE_DATA, event).sendToTarget();
+    }
+
+    /**
+     * DataSource calls it to transmit list of events.
+     */
+    public void addBatchData(List<ForensicEvent> events) {
+        mHandler.obtainMessage(MSG_BATCH_DATA, events).sendToTarget();
+    }
+
+    /**
+     * Disable the data collection of all DataSources.
+     */
+    public void disable() {
+        mHandler.obtainMessage(MSG_DISABLE).sendToTarget();
+        for (DataSource ds : mDataSources) {
+            ds.disable();
+        }
+    }
+
+    private void onNewSingleData(ForensicEvent event) {
+        if (mStoredEvents.size() < STORED_EVENTS_SIZE_LIMIT) {
+            mStoredEvents.add(event);
+        } else {
+            mForensicService.addNewData(mStoredEvents);
+            mStoredEvents = new ArrayList<>();
+        }
+    }
+
+    private void onNewBatchData(List<ForensicEvent> events) {
+        mForensicService.addNewData(events);
+    }
+
+    private void onDisable() {
+        for (DataSource ds : mDataSources) {
+            ds.disable();
+        }
+        mHandlerThread.quitSafely();
+        mHandlerThread = null;
+    }
+
+    private static class EventHandler extends Handler {
+        private final DataAggregator mDataAggregator;
+        EventHandler(Looper looper, DataAggregator dataAggregator) {
+            super(looper);
+            mDataAggregator = dataAggregator;
+        }
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case MSG_SINGLE_DATA:
+                    mDataAggregator.onNewSingleData((ForensicEvent) msg.obj);
+                    break;
+                case MSG_BATCH_DATA:
+                    mDataAggregator.onNewBatchData((List<ForensicEvent>) msg.obj);
+                    break;
+                case MSG_DISABLE:
+                    mDataAggregator.onDisable();
+                    break;
+                default:
+                    Slog.w(TAG, "Unknown message: " + msg.what);
+            }
+        }
+    }
+}
diff --git a/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodRunnerState.java b/services/core/java/com/android/server/security/forensic/DataSource.java
similarity index 73%
rename from ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodRunnerState.java
rename to services/core/java/com/android/server/security/forensic/DataSource.java
index 83cbc52..da7ee21 100644
--- a/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodRunnerState.java
+++ b/services/core/java/com/android/server/security/forensic/DataSource.java
@@ -13,10 +13,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package android.platform.test.ravenwood;
 
-/** Stub class. The actual implementaetion is in junit-impl-src. */
-public class RavenwoodRunnerState {
-    public RavenwoodRunnerState(RavenwoodAwareTestRunner runner) {
-    }
+package com.android.server.security.forensic;
+
+public interface DataSource {
+    /**
+     * Enable the data collection.
+     */
+    void enable();
+
+    /**
+     * Disable the data collection.
+     */
+    void disable();
 }
diff --git a/services/core/java/com/android/server/security/forensic/ForensicService.java b/services/core/java/com/android/server/security/forensic/ForensicService.java
index 20c648e..01f630b 100644
--- a/services/core/java/com/android/server/security/forensic/ForensicService.java
+++ b/services/core/java/com/android/server/security/forensic/ForensicService.java
@@ -22,6 +22,7 @@
 import android.os.Looper;
 import android.os.Message;
 import android.os.RemoteException;
+import android.security.forensic.ForensicEvent;
 import android.security.forensic.IForensicService;
 import android.security.forensic.IForensicServiceCommandCallback;
 import android.security.forensic.IForensicServiceStateCallback;
@@ -32,6 +33,7 @@
 import com.android.server.SystemService;
 
 import java.util.ArrayList;
+import java.util.List;
 
 /**
  * @hide
@@ -64,6 +66,7 @@
     private final Context mContext;
     private final Handler mHandler;
     private final BackupTransportConnection mBackupTransportConnection;
+    private final DataAggregator mDataAggregator;
     private final BinderService mBinderService;
 
     private final ArrayList<IForensicServiceStateCallback> mStateMonitors = new ArrayList<>();
@@ -79,6 +82,7 @@
         mContext = injector.getContext();
         mHandler = new EventHandler(injector.getLooper(), this);
         mBackupTransportConnection = injector.getBackupTransportConnection();
+        mDataAggregator = injector.getDataAggregator(this);
         mBinderService = new BinderService(this);
     }
 
@@ -167,6 +171,9 @@
                         Slog.e(TAG, "RemoteException", e);
                     }
                     break;
+                case MSG_BACKUP:
+                    mService.backup((List<ForensicEvent>) msg.obj);
+                    break;
                 default:
                     Slog.w(TAG, "Unknown message: " + msg.what);
             }
@@ -192,6 +199,10 @@
     private void makeVisible(IForensicServiceCommandCallback callback) throws RemoteException {
         switch (mState) {
             case STATE_INVISIBLE:
+                if (!mDataAggregator.initialize()) {
+                    callback.onFailure(ERROR_DATA_SOURCE_UNAVAILABLE);
+                    break;
+                }
                 mState = STATE_VISIBLE;
                 notifyStateMonitors();
                 callback.onSuccess();
@@ -227,6 +238,7 @@
                     callback.onFailure(ERROR_BACKUP_TRANSPORT_UNAVAILABLE);
                     break;
                 }
+                mDataAggregator.enable();
                 mState = STATE_ENABLED;
                 notifyStateMonitors();
                 callback.onSuccess();
@@ -243,6 +255,7 @@
         switch (mState) {
             case STATE_ENABLED:
                 mBackupTransportConnection.release();
+                mDataAggregator.disable();
                 mState = STATE_VISIBLE;
                 notifyStateMonitors();
                 callback.onSuccess();
@@ -255,6 +268,17 @@
         }
     }
 
+    /**
+     * Add a list of ForensicEvent.
+     */
+    public void addNewData(List<ForensicEvent> events) {
+        mHandler.obtainMessage(MSG_BACKUP, events).sendToTarget();
+    }
+
+    private void backup(List<ForensicEvent> events) {
+        mBackupTransportConnection.addData(events);
+    }
+
     @Override
     public void onStart() {
         try {
@@ -275,6 +299,8 @@
         Looper getLooper();
 
         BackupTransportConnection getBackupTransportConnection();
+
+        DataAggregator getDataAggregator(ForensicService forensicService);
     }
 
     private static final class InjectorImpl implements Injector {
@@ -303,6 +329,11 @@
         public BackupTransportConnection getBackupTransportConnection() {
             return new BackupTransportConnection(mContext);
         }
+
+        @Override
+        public DataAggregator getDataAggregator(ForensicService forensicService) {
+            return new DataAggregator(mContext, forensicService);
+        }
     }
 }
 
diff --git a/services/core/java/com/android/server/security/forensic/SecurityLogSource.java b/services/core/java/com/android/server/security/forensic/SecurityLogSource.java
new file mode 100644
index 0000000..0f1aa42
--- /dev/null
+++ b/services/core/java/com/android/server/security/forensic/SecurityLogSource.java
@@ -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.server.security.forensic;
+
+import android.Manifest.permission;
+import android.annotation.RequiresPermission;
+import android.app.admin.DevicePolicyManager;
+import android.app.admin.SecurityLog.SecurityEvent;
+import android.content.Context;
+import android.security.forensic.ForensicEvent;
+import android.util.ArrayMap;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+import java.util.function.Consumer;
+import java.util.stream.Collectors;
+
+public class SecurityLogSource implements DataSource {
+
+    private static final String TAG = "Forensic SecurityLogSource";
+    private static final String EVENT_TYPE = "SecurityEvent";
+    private static final String EVENT_TAG = "TAG";
+    private static final String EVENT_TIME = "TIME";
+    private static final String EVENT_DATA = "DATA";
+
+    private SecurityEventCallback mEventCallback = new SecurityEventCallback();
+    private DevicePolicyManager mDpm;
+    private Executor mExecutor;
+    private DataAggregator mDataAggregator;
+
+    public SecurityLogSource(Context context, DataAggregator dataAggregator) {
+        mDataAggregator = dataAggregator;
+        mDpm = context.getSystemService(DevicePolicyManager.class);
+        mExecutor = Executors.newSingleThreadExecutor();
+        mEventCallback = new SecurityEventCallback();
+    }
+
+    @Override
+    @RequiresPermission(permission.MANAGE_DEVICE_POLICY_AUDIT_LOGGING)
+    public void enable() {
+        enableAuditLog();
+        mDpm.setAuditLogEventCallback(mExecutor, mEventCallback);
+    }
+
+    @Override
+    @RequiresPermission(permission.MANAGE_DEVICE_POLICY_AUDIT_LOGGING)
+    public void disable() {
+        disableAuditLog();
+    }
+
+    @RequiresPermission(permission.MANAGE_DEVICE_POLICY_AUDIT_LOGGING)
+    private void enableAuditLog() {
+        if (!isAuditLogEnabled()) {
+            mDpm.setAuditLogEnabled(true);
+        }
+    }
+
+    @RequiresPermission(permission.MANAGE_DEVICE_POLICY_AUDIT_LOGGING)
+    private void disableAuditLog() {
+        if (isAuditLogEnabled()) {
+            mDpm.setAuditLogEnabled(false);
+        }
+    }
+
+    /**
+     * Check if security audit logging is enabled for the caller.
+     *
+     * @return Whether security audit logging is enabled.
+     */
+    public boolean isAuditLogEnabled() {
+        return mDpm.isAuditLogEnabled();
+    }
+
+    private class SecurityEventCallback implements Consumer<List<SecurityEvent>> {
+
+        @Override
+        public void accept(List<SecurityEvent> events) {
+            List<ForensicEvent> forensicEvents =
+                    events.stream()
+                            .filter(event -> event != null)
+                            .map(event -> toForensicEvent(event))
+                            .collect(Collectors.toList());
+            mDataAggregator.addBatchData(forensicEvents);
+        }
+
+        private ForensicEvent toForensicEvent(SecurityEvent event) {
+            ArrayMap<String, String> keyValuePairs = new ArrayMap<>();
+            keyValuePairs.put(EVENT_TIME, String.valueOf(event.getTimeNanos()));
+            // TODO: Map tag to corresponding string
+            keyValuePairs.put(EVENT_TAG, String.valueOf(event.getTag()));
+            keyValuePairs.put(EVENT_DATA, eventDataToString(event.getData()));
+            return new ForensicEvent(EVENT_TYPE, keyValuePairs);
+        }
+
+        /**
+         * Convert event data to a String.
+         *
+         * @param obj Object containing an Integer, Long, Float, String, null, or Object[] of the
+         *     same.
+         * @return String representation of event data.
+         */
+        private String eventDataToString(Object obj) {
+            if (obj == null) {
+                return "";
+            } else if (obj instanceof Integer
+                    || obj instanceof Long
+                    || obj instanceof Float
+                    || obj instanceof String) {
+                return String.valueOf(obj);
+            } else if (obj instanceof Object[]) {
+                Object[] objArray = (Object[]) obj;
+                String[] strArray = new String[objArray.length];
+                for (int i = 0; i < objArray.length; ++i) {
+                    strArray[i] = eventDataToString(objArray[i]);
+                }
+                return Arrays.toString((String[]) strArray);
+            } else {
+                throw new IllegalArgumentException(
+                        "Unsupported data type: " + obj.getClass().getSimpleName());
+            }
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/slice/SliceManagerService.java b/services/core/java/com/android/server/slice/SliceManagerService.java
index 881bdbd..15fd35e 100644
--- a/services/core/java/com/android/server/slice/SliceManagerService.java
+++ b/services/core/java/com/android/server/slice/SliceManagerService.java
@@ -604,6 +604,11 @@
     private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
+            final String action = intent.getAction();
+            if (action == null) {
+                Slog.w(TAG, "Intent broadcast does not contain action: " + intent);
+                return;
+            }
             final int userId  = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
             if (userId == UserHandle.USER_NULL) {
                 Slog.w(TAG, "Intent broadcast does not contain user handle: " + intent);
@@ -615,7 +620,7 @@
                 Slog.w(TAG, "Intent broadcast does not contain package name: " + intent);
                 return;
             }
-            switch (intent.getAction()) {
+            switch (action) {
                 case Intent.ACTION_PACKAGE_REMOVED:
                     final boolean replacing =
                             intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
diff --git a/services/core/java/com/android/server/vcn/OWNERS b/services/core/java/com/android/server/vcn/OWNERS
index 2441e77..937699a 100644
--- a/services/core/java/com/android/server/vcn/OWNERS
+++ b/services/core/java/com/android/server/vcn/OWNERS
@@ -1,7 +1,6 @@
 set noparent
 
-benedictwong@google.com
-ckesting@google.com
 evitayan@google.com
-junyin@google.com
 nharold@google.com
+benedictwong@google.com #{LAST_RESORT_SUGGESTION}
+yangji@google.com #{LAST_RESORT_SUGGESTION}
diff --git a/services/core/java/com/android/server/vibrator/ComposePwleV2VibratorStep.java b/services/core/java/com/android/server/vibrator/ComposePwleV2VibratorStep.java
new file mode 100644
index 0000000..d0d6071
--- /dev/null
+++ b/services/core/java/com/android/server/vibrator/ComposePwleV2VibratorStep.java
@@ -0,0 +1,153 @@
+/*
+ * 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.vibrator;
+
+import android.annotation.NonNull;
+import android.os.Trace;
+import android.os.VibrationEffect;
+import android.os.vibrator.Flags;
+import android.os.vibrator.PwleSegment;
+import android.os.vibrator.VibrationEffectSegment;
+import android.util.Slog;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Represents a step to turn the vibrator on using a composition of PWLE segments.
+ *
+ * <p>This step will use the maximum supported number of consecutive segments of type
+ * {@link PwleSegment}, starting at the current index.
+ */
+final class ComposePwleV2VibratorStep extends AbstractComposedVibratorStep {
+
+    ComposePwleV2VibratorStep(VibrationStepConductor conductor, long startTime,
+            VibratorController controller, VibrationEffect.Composed effect, int index,
+            long pendingVibratorOffDeadline) {
+        // This step should wait for the last vibration to finish (with the timeout) and for the
+        // intended step start time (to respect the effect delays).
+        super(conductor, Math.max(startTime, pendingVibratorOffDeadline), controller, effect,
+                index, pendingVibratorOffDeadline);
+    }
+
+    @NonNull
+    @Override
+    public List<Step> play() {
+        if (!Flags.normalizedPwleEffects()) {
+            // Skip this step and play the next one right away.
+            return nextSteps(/* segmentsPlayed= */ 1);
+        }
+
+        Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "ComposePwleV2Step");
+        try {
+            // Load the next PwleSegments to create a single composePwleV2 call to the vibrator,
+            // limited to the vibrator's maximum envelope effect size.
+            int limit = controller.getVibratorInfo().getMaxEnvelopeEffectSize();
+            List<PwleSegment> pwles = unrollPwleSegments(effect, segmentIndex, limit);
+
+            if (pwles.isEmpty()) {
+                Slog.w(VibrationThread.TAG, "Ignoring wrong segment for a ComposeEnvelopeStep: "
+                        + effect.getSegments().get(segmentIndex));
+                // Skip this step and play the next one right away.
+                return nextSteps(/* segmentsPlayed= */ 1);
+            }
+
+            if (VibrationThread.DEBUG) {
+                Slog.d(VibrationThread.TAG, "Compose " + pwles + " PWLEs on vibrator "
+                        + controller.getVibratorInfo().getId());
+            }
+            PwleSegment[] pwlesArray = pwles.toArray(new PwleSegment[pwles.size()]);
+            long vibratorOnResult = controller.on(pwlesArray, getVibration().id);
+            handleVibratorOnResult(vibratorOnResult);
+            getVibration().stats.reportComposePwle(vibratorOnResult, pwlesArray);
+
+            // The next start and off times will be calculated from mVibratorOnResult.
+            return nextSteps(/* segmentsPlayed= */ pwles.size());
+        } finally {
+            Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
+        }
+    }
+
+    private List<PwleSegment> unrollPwleSegments(VibrationEffect.Composed effect, int startIndex,
+            int limit) {
+        List<PwleSegment> segments = new ArrayList<>(limit);
+        float bestBreakAmplitude = 1;
+        int bestBreakPosition = limit; // Exclusive index.
+
+        int segmentCount = effect.getSegments().size();
+        int repeatIndex = effect.getRepeatIndex();
+
+        // Loop once after reaching the limit to see if breaking it will really be necessary, then
+        // apply the best break position found, otherwise return the full list as it fits the limit.
+        for (int i = startIndex; segments.size() <= limit; i++) {
+            if (i == segmentCount) {
+                if (repeatIndex >= 0) {
+                    i = repeatIndex;
+                } else {
+                    // Non-repeating effect, stop collecting pwles.
+                    break;
+                }
+            }
+            VibrationEffectSegment segment = effect.getSegments().get(i);
+            if (segment instanceof PwleSegment pwleSegment) {
+                segments.add(pwleSegment);
+
+                if (isBetterBreakPosition(segments, bestBreakAmplitude, limit)) {
+                    // Mark this position as the best one so far to break a long waveform.
+                    bestBreakAmplitude = pwleSegment.getEndAmplitude();
+                    bestBreakPosition = segments.size(); // Break after this pwle ends.
+                }
+            } else {
+                // First non-pwle segment, stop collecting pwles.
+                break;
+            }
+        }
+
+        return segments.size() > limit
+                // Remove excessive segments, using the best breaking position recorded.
+                ? segments.subList(0, bestBreakPosition)
+                // Return all collected pwle segments.
+                : segments;
+    }
+
+    /**
+     * Returns true if the current segment list represents a better break position for a PWLE,
+     * given the current amplitude being used for breaking it at a smaller size and the size limit.
+     */
+    private boolean isBetterBreakPosition(List<PwleSegment> segments,
+            float currentBestBreakAmplitude, int limit) {
+        PwleSegment lastSegment = segments.get(segments.size() - 1);
+        float breakAmplitudeCandidate = lastSegment.getEndAmplitude();
+        int breakPositionCandidate = segments.size();
+
+        if (breakPositionCandidate > limit) {
+            // We're beyond limit, last break position found should be used.
+            return false;
+        }
+        if (breakAmplitudeCandidate == 0) {
+            // Breaking at amplitude zero at any position is always preferable.
+            return true;
+        }
+        if (breakPositionCandidate < limit / 2) {
+            // Avoid breaking at the first half of the allowed maximum size, even if amplitudes are
+            // lower, to avoid creating PWLEs that are too small unless it's to break at zero.
+            return false;
+        }
+        // Prefer lower amplitudes at a later position for breaking the PWLE in a more subtle way.
+        return breakAmplitudeCandidate <= currentBestBreakAmplitude;
+    }
+}
diff --git a/services/core/java/com/android/server/vibrator/DeviceAdapter.java b/services/core/java/com/android/server/vibrator/DeviceAdapter.java
index bd4fc07..751e83c 100644
--- a/services/core/java/com/android/server/vibrator/DeviceAdapter.java
+++ b/services/core/java/com/android/server/vibrator/DeviceAdapter.java
@@ -47,6 +47,11 @@
      * instance is created with the final segment list.
      */
     private final List<VibrationSegmentsAdapter> mSegmentAdapters;
+    /**
+     * The vibration segment validators that can validate VibrationEffectSegments entries based on
+     * the VibratorInfo.
+     */
+    private final List<VibrationSegmentsValidator> mSegmentsValidators;
 
     DeviceAdapter(VibrationSettings settings, SparseArray<VibratorController> vibrators) {
         mSegmentAdapters = Arrays.asList(
@@ -60,7 +65,13 @@
                 // Split segments based on their duration and device supported limits
                 new SplitSegmentsAdapter(),
                 // Clip amplitudes and frequencies of final segments based on device bandwidth curve
-                new ClippingAmplitudeAndFrequencyAdapter()
+                new ClippingAmplitudeAndFrequencyAdapter(),
+                // Split Pwle segments based on their duration and device supported limits
+                new SplitPwleSegmentsAdapter()
+        );
+        mSegmentsValidators = List.of(
+                // Validate Pwle segments base on the vibrators frequency range
+                new PwleSegmentsValidator()
         );
         mAvailableVibrators = vibrators;
         mAvailableVibratorIds = new int[vibrators.size()];
@@ -78,7 +89,6 @@
         return mAvailableVibratorIds;
     }
 
-    @NonNull
     @Override
     public VibrationEffect adaptToVibrator(int vibratorId, @NonNull VibrationEffect effect) {
         if (!(effect instanceof VibrationEffect.Composed composed)) {
@@ -102,6 +112,14 @@
                     mSegmentAdapters.get(i).adaptToVibrator(info, newSegments, newRepeatIndex);
         }
 
+        // Validate the vibration segments. If a segment is not supported, ignore the entire
+        // vibration effect.
+        for (int i = 0; i < mSegmentsValidators.size(); i++) {
+            if (!mSegmentsValidators.get(i).hasValidSegments(info, newSegments)) {
+                return null;
+            }
+        }
+
         return new VibrationEffect.Composed(newSegments, newRepeatIndex);
     }
 }
diff --git a/services/core/java/com/android/server/vibrator/HalVibration.java b/services/core/java/com/android/server/vibrator/HalVibration.java
index d192e64..c9f1e4b 100644
--- a/services/core/java/com/android/server/vibrator/HalVibration.java
+++ b/services/core/java/com/android/server/vibrator/HalVibration.java
@@ -124,12 +124,18 @@
      * @param deviceAdapter A {@link CombinedVibration.VibratorAdapter} that transforms vibration
      *                      effects to device vibrators based on its capabilities.
      */
-    public void adaptToDevice(CombinedVibration.VibratorAdapter deviceAdapter) {
-        CombinedVibration newEffect = mEffectToPlay.adapt(deviceAdapter);
-        if (!Objects.equals(mEffectToPlay, newEffect)) {
-            mEffectToPlay = newEffect;
+    public boolean adaptToDevice(CombinedVibration.VibratorAdapter deviceAdapter) {
+        CombinedVibration adaptedEffect = mEffectToPlay.adapt(deviceAdapter);
+        if (adaptedEffect == null) {
+            return false;
+        }
+
+        if (!mEffectToPlay.equals(adaptedEffect)) {
+            mEffectToPlay = adaptedEffect;
         }
         // No need to update fallback effects, they are already configured per device.
+
+        return true;
     }
 
     /** Return the effect that should be played by this vibration. */
diff --git a/services/core/java/com/android/server/vibrator/PwleSegmentsValidator.java b/services/core/java/com/android/server/vibrator/PwleSegmentsValidator.java
new file mode 100644
index 0000000..87369aa
--- /dev/null
+++ b/services/core/java/com/android/server/vibrator/PwleSegmentsValidator.java
@@ -0,0 +1,60 @@
+/*
+ * 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.vibrator;
+
+import android.hardware.vibrator.IVibrator;
+import android.os.VibratorInfo;
+import android.os.vibrator.PwleSegment;
+import android.os.vibrator.VibrationEffectSegment;
+
+import java.util.List;
+
+/**
+ * Validates Pwle segments to ensure they are compatible with the device's capabilities
+ * and adhere to frequency constraints.
+ *
+ * <p>The validator verifies that each segment's start and end frequencies fall within
+ * the supported range.
+ *
+ * <p>The segments will be considered invalid of the device does not have
+ * {@link IVibrator#CAP_COMPOSE_PWLE_EFFECTS_V2}.
+ */
+final class PwleSegmentsValidator implements VibrationSegmentsValidator {
+
+    @Override
+    public boolean hasValidSegments(VibratorInfo info, List<VibrationEffectSegment> segments) {
+
+        boolean hasPwleCapability = info.hasCapability(IVibrator.CAP_COMPOSE_PWLE_EFFECTS_V2);
+        float minFrequency = info.getFrequencyProfile().getMinFrequencyHz();
+        float maxFrequency = info.getFrequencyProfile().getMaxFrequencyHz();
+
+        for (VibrationEffectSegment segment : segments) {
+            if (!(segment instanceof PwleSegment pwleSegment)) {
+                continue;
+            }
+
+            if (!hasPwleCapability || pwleSegment.getStartFrequencyHz() < minFrequency
+                    || pwleSegment.getStartFrequencyHz() > maxFrequency
+                    || pwleSegment.getEndFrequencyHz() < minFrequency
+                    || pwleSegment.getEndFrequencyHz() > maxFrequency) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+}
diff --git a/services/core/java/com/android/server/vibrator/SplitPwleSegmentsAdapter.java b/services/core/java/com/android/server/vibrator/SplitPwleSegmentsAdapter.java
new file mode 100644
index 0000000..ad44227
--- /dev/null
+++ b/services/core/java/com/android/server/vibrator/SplitPwleSegmentsAdapter.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.vibrator;
+
+import android.hardware.vibrator.IVibrator;
+import android.os.VibratorInfo;
+import android.os.vibrator.PwleSegment;
+import android.os.vibrator.VibrationEffectSegment;
+import android.util.MathUtils;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Adapter that splits Pwle segments with longer duration than the device capabilities.
+ *
+ * <p>This transformation replaces large {@link android.os.vibrator.PwleSegment} entries by a
+ * sequence of smaller segments that starts and ends at the same amplitudes/frequencies,
+ * interpolating the intermediate values.
+ *
+ * <p>The segments will not be changed if the device doesn't have
+ * {@link IVibrator#CAP_COMPOSE_PWLE_EFFECTS_V2}.
+ */
+final class SplitPwleSegmentsAdapter implements VibrationSegmentsAdapter {
+
+    @Override
+    public int adaptToVibrator(VibratorInfo info, List<VibrationEffectSegment> segments,
+            int repeatIndex) {
+        if (!info.hasCapability(IVibrator.CAP_COMPOSE_PWLE_EFFECTS_V2)) {
+            // The vibrator does not have PWLE v2 capability, so keep the segments unchanged.
+            return repeatIndex;
+        }
+        int maxPwleDuration = info.getMaxEnvelopeEffectDurationMillis();
+        if (maxPwleDuration <= 0) {
+            // No limit set to PWLE primitive duration.
+            return repeatIndex;
+        }
+
+        int segmentCount = segments.size();
+        for (int i = 0; i < segmentCount; i++) {
+            if (!(segments.get(i) instanceof PwleSegment pwleSegment)) {
+                continue;
+            }
+            int splits = ((int) pwleSegment.getDuration() + maxPwleDuration - 1) / maxPwleDuration;
+            if (splits <= 1) {
+                continue;
+            }
+            segments.remove(i);
+            segments.addAll(i, splitPwleSegment(pwleSegment, splits));
+            int addedSegments = splits - 1;
+            if (repeatIndex > i) {
+                repeatIndex += addedSegments;
+            }
+            i += addedSegments;
+            segmentCount += addedSegments;
+        }
+
+        return repeatIndex;
+    }
+
+    private static List<PwleSegment> splitPwleSegment(PwleSegment pwleSegment,
+            int splits) {
+        List<PwleSegment> pwleSegments = new ArrayList<>(splits);
+        float startFrequencyHz = pwleSegment.getStartFrequencyHz();
+        float endFrequencyHz = pwleSegment.getEndFrequencyHz();
+        long splitDuration = pwleSegment.getDuration() / splits;
+        float previousAmplitude = pwleSegment.getStartAmplitude();
+        float previousFrequencyHz = startFrequencyHz;
+        long accumulatedDuration = 0;
+
+        for (int i = 1; i < splits; i++) {
+            accumulatedDuration += splitDuration;
+            float durationRatio = (float) accumulatedDuration / pwleSegment.getDuration();
+            float interpolatedFrequency =
+                    MathUtils.lerp(startFrequencyHz, endFrequencyHz, durationRatio);
+            float interpolatedAmplitude = MathUtils.lerp(pwleSegment.getStartAmplitude(),
+                    pwleSegment.getEndAmplitude(), durationRatio);
+            PwleSegment pwleSplit = new PwleSegment(
+                    previousAmplitude, interpolatedAmplitude,
+                    previousFrequencyHz, interpolatedFrequency,
+                    (int) splitDuration);
+            pwleSegments.add(pwleSplit);
+            previousAmplitude = pwleSplit.getEndAmplitude();
+            previousFrequencyHz = pwleSplit.getEndFrequencyHz();
+        }
+
+        pwleSegments.add(
+                new PwleSegment(previousAmplitude, pwleSegment.getEndAmplitude(),
+                        previousFrequencyHz, endFrequencyHz,
+                        (int) (pwleSegment.getDuration() - accumulatedDuration)));
+
+        return pwleSegments;
+    }
+}
diff --git a/services/core/java/com/android/server/vibrator/VibrationSegmentsValidator.java b/services/core/java/com/android/server/vibrator/VibrationSegmentsValidator.java
new file mode 100644
index 0000000..75002bf
--- /dev/null
+++ b/services/core/java/com/android/server/vibrator/VibrationSegmentsValidator.java
@@ -0,0 +1,35 @@
+/*
+ * 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.vibrator;
+
+import android.os.VibratorInfo;
+import android.os.vibrator.VibrationEffectSegment;
+
+import java.util.List;
+
+/** Validates a sequence of {@link VibrationEffectSegment}s for a vibrator. */
+public interface VibrationSegmentsValidator {
+    /**
+     * Checks whether the vibrator can play the provided segments based on the given
+     * {@link VibratorInfo}.
+     *
+     * @param info     The vibrator info to be applied to the sequence of segments.
+     * @param segments List of {@link VibrationEffectSegment} to be checked.
+     * @return True if vibrator can play the effect, false otherwise.
+     */
+    boolean hasValidSegments(VibratorInfo info, List<VibrationEffectSegment> segments);
+}
diff --git a/services/core/java/com/android/server/vibrator/VibrationStats.java b/services/core/java/com/android/server/vibrator/VibrationStats.java
index 637a5a1..bc4dbe7 100644
--- a/services/core/java/com/android/server/vibrator/VibrationStats.java
+++ b/services/core/java/com/android/server/vibrator/VibrationStats.java
@@ -22,6 +22,7 @@
 import android.os.SystemClock;
 import android.os.vibrator.PrebakedSegment;
 import android.os.vibrator.PrimitiveSegment;
+import android.os.vibrator.PwleSegment;
 import android.os.vibrator.RampSegment;
 import android.util.Slog;
 import android.util.SparseBooleanArray;
@@ -292,6 +293,25 @@
         }
     }
 
+    /** Report a call to vibrator method to trigger a vibration as a PWLE. */
+    void reportComposePwle(long halResult, PwleSegment[] segments) {
+        mVibratorComposePwleCount++;
+        mVibrationPwleTotalSize += segments.length;
+
+        if (halResult > 0) {
+            // If HAL result is positive then it represents the actual duration of the vibration.
+            // Remove the zero-amplitude segments to update the total time the vibrator was ON.
+            for (PwleSegment ramp : segments) {
+                if ((ramp.getStartAmplitude() == 0) && (ramp.getEndAmplitude() == 0)) {
+                    halResult -= ramp.getDuration();
+                }
+            }
+            if (halResult > 0) {
+                mVibratorOnTotalDurationMillis += (int) halResult;
+            }
+        }
+    }
+
     /**
      * Increment the stats for total number of times the {@code setExternalControl} method was
      * triggered in the vibrator HAL.
diff --git a/services/core/java/com/android/server/vibrator/VibrationStepConductor.java b/services/core/java/com/android/server/vibrator/VibrationStepConductor.java
index 4bb0c16..6a4790d 100644
--- a/services/core/java/com/android/server/vibrator/VibrationStepConductor.java
+++ b/services/core/java/com/android/server/vibrator/VibrationStepConductor.java
@@ -24,6 +24,7 @@
 import android.os.vibrator.Flags;
 import android.os.vibrator.PrebakedSegment;
 import android.os.vibrator.PrimitiveSegment;
+import android.os.vibrator.PwleSegment;
 import android.os.vibrator.RampSegment;
 import android.os.vibrator.VibrationEffectSegment;
 import android.util.IntArray;
@@ -166,12 +167,20 @@
             return new ComposePwleVibratorStep(this, startTime, controller, effect, segmentIndex,
                     pendingVibratorOffDeadline);
         }
+        if (segment instanceof PwleSegment) {
+            return new ComposePwleV2VibratorStep(this, startTime, controller, effect,
+                    segmentIndex, pendingVibratorOffDeadline);
+        }
         return new SetAmplitudeVibratorStep(this, startTime, controller, effect, segmentIndex,
                 pendingVibratorOffDeadline);
     }
 
-    /** Called when this conductor is going to be started running by the VibrationThread. */
-    public void prepareToStart() {
+    /**
+     * Called when this conductor is going to be started running by the VibrationThread.
+     *
+     * @return True if the vibration effect can be played, false otherwise.
+     */
+    public boolean prepareToStart() {
         if (Build.IS_DEBUGGABLE) {
             expectIsVibrationThread(true);
         }
@@ -182,7 +191,11 @@
         // Scale resolves the default amplitudes from the effect before scaling them.
         mVibration.scaleEffects(mVibrationScaler);
 
-        mVibration.adaptToDevice(mDeviceAdapter);
+        if (!mVibration.adaptToDevice(mDeviceAdapter)) {
+            // Unable to adapt vibration effect for playback. This likely indicates the presence
+            // of unsupported segments. The original effect will be ignored.
+            return false;
+        }
         CombinedVibration.Sequential sequentialEffect = toSequential(mVibration.getEffectToPlay());
         mPendingVibrateSteps++;
         // This count is decremented at the completion of the step, so we don't subtract one.
@@ -191,6 +204,8 @@
         // Vibration will start playing in the Vibrator, following the effect timings and delays.
         // Report current time as the vibration start time, for debugging.
         mVibration.stats.reportStarted();
+
+        return true;
     }
 
     public HalVibration getVibration() {
diff --git a/services/core/java/com/android/server/vibrator/VibrationThread.java b/services/core/java/com/android/server/vibrator/VibrationThread.java
index 5b22c10..cb9988f 100644
--- a/services/core/java/com/android/server/vibrator/VibrationThread.java
+++ b/services/core/java/com/android/server/vibrator/VibrationThread.java
@@ -263,7 +263,15 @@
     private void playVibration() {
         Trace.traceBegin(TRACE_TAG_VIBRATOR, "playVibration");
         try {
-            mExecutingConductor.prepareToStart();
+            if (!mExecutingConductor.prepareToStart()) {
+                // The effect cannot be played, start clean-up tasks and notify
+                // callback immediately.
+                clientVibrationCompleteIfNotAlready(
+                        new Vibration.EndInfo(Status.IGNORED_UNSUPPORTED));
+
+                return;
+            }
+
             while (!mExecutingConductor.isFinished()) {
                 boolean readyToRun = mExecutingConductor.waitUntilNextStepIsDue();
                 // If we waited, don't run the next step, but instead re-evaluate status.
diff --git a/services/core/java/com/android/server/vibrator/VibratorController.java b/services/core/java/com/android/server/vibrator/VibratorController.java
index 6aed00e..f78bff8 100644
--- a/services/core/java/com/android/server/vibrator/VibratorController.java
+++ b/services/core/java/com/android/server/vibrator/VibratorController.java
@@ -30,6 +30,7 @@
 import android.os.VibratorInfo;
 import android.os.vibrator.PrebakedSegment;
 import android.os.vibrator.PrimitiveSegment;
+import android.os.vibrator.PwleSegment;
 import android.os.vibrator.RampSegment;
 import android.util.IndentingPrintWriter;
 import android.util.Slog;
@@ -414,6 +415,33 @@
     }
 
     /**
+     * Plays a composition of pwle v2 primitives, using {@code vibrationId} for completion callback
+     * to {@link OnVibrationCompleteListener}.
+     *
+     * <p>This will affect the state of {@link #isVibrating()}.
+     *
+     * @return The duration of the effect playing, or 0 if unsupported.
+     */
+    public long on(PwleSegment[] primitives, long vibrationId) {
+        Trace.traceBegin(TRACE_TAG_VIBRATOR, "VibratorController#on (PWLE v2)");
+        try {
+            if (!mVibratorInfo.hasCapability(IVibrator.CAP_COMPOSE_PWLE_EFFECTS_V2)) {
+                return 0;
+            }
+            synchronized (mLock) {
+                long duration = mNativeWrapper.composePwleV2(primitives, vibrationId);
+                if (duration > 0) {
+                    mCurrentAmplitude = -1;
+                    updateStateAndNotifyListenersLocked(VibratorState.VIBRATING);
+                }
+                return duration;
+            }
+        } finally {
+            Trace.traceEnd(TRACE_TAG_VIBRATOR);
+        }
+    }
+
+    /**
      * Turns off the vibrator and disables completion callback to any pending vibration.
      *
      * <p>This will affect the state of {@link #isVibrating()}.
@@ -534,6 +562,9 @@
         private static native long performPwleEffect(long nativePtr, RampSegment[] effect,
                 int braking, long vibrationId);
 
+        private static native long performPwleV2Effect(long nativePtr, PwleSegment[] effect,
+                long vibrationId);
+
         private static native void setExternalControl(long nativePtr, boolean enabled);
 
         private static native void alwaysOnEnable(long nativePtr, long id, long effect,
@@ -600,6 +631,11 @@
             return performPwleEffect(mNativePtr, primitives, braking, vibrationId);
         }
 
+        /** Turns vibrator on to perform PWLE effect composed of given primitives. */
+        public long composePwleV2(PwleSegment[] primitives, long vibrationId) {
+            return performPwleV2Effect(mNativePtr, primitives, vibrationId);
+        }
+
         /** Enabled the device vibrator to be controlled by another service. */
         public void setExternalControl(boolean enabled) {
             setExternalControl(mNativePtr, enabled);
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperData.java b/services/core/java/com/android/server/wallpaper/WallpaperData.java
index 15f86e9..c8d5a03 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperData.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperData.java
@@ -29,6 +29,7 @@
 import android.app.WallpaperColors;
 import android.app.WallpaperManager.ScreenOrientation;
 import android.app.WallpaperManager.SetWallpaperFlags;
+import android.app.wallpaper.WallpaperDescription;
 import android.content.ComponentName;
 import android.graphics.Rect;
 import android.os.RemoteCallbackList;
@@ -77,6 +78,8 @@
 
     /**
      * The component name of the currently set live wallpaper.
+     *
+     * @deprecated
      */
     private ComponentName mWallpaperComponent;
 
@@ -179,6 +182,9 @@
      */
     int mOrientationWhenSet = ORIENTATION_UNKNOWN;
 
+    /** Description of the current wallpaper */
+    private WallpaperDescription mDescription;
+
     WallpaperData(int userId, @SetWallpaperFlags int wallpaperType) {
         this.userId = userId;
         this.mWhich = wallpaperType;
@@ -238,6 +244,14 @@
         this.mWallpaperComponent = componentName;
     }
 
+    WallpaperDescription getDescription() {
+        return mDescription;
+    }
+
+    void setDescription(WallpaperDescription description) {
+        this.mDescription = description;
+    }
+
     @Override
     public String toString() {
         StringBuilder out = new StringBuilder(defaultString(this));
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperDataParser.java b/services/core/java/com/android/server/wallpaper/WallpaperDataParser.java
index 74ca230..cf76bf0 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperDataParser.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperDataParser.java
@@ -16,6 +16,7 @@
 
 package com.android.server.wallpaper;
 
+import static android.app.Flags.liveWallpaperContentHandling;
 import static android.app.Flags.removeNextWallpaperComponent;
 import static android.app.WallpaperManager.FLAG_LOCK;
 import static android.app.WallpaperManager.FLAG_SYSTEM;
@@ -30,11 +31,13 @@
 import static com.android.server.wallpaper.WallpaperUtils.makeWallpaperIdLocked;
 import static com.android.window.flags.Flags.multiCrop;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.WallpaperColors;
 import android.app.WallpaperManager;
 import android.app.WallpaperManager.SetWallpaperFlags;
 import android.app.backup.WallpaperBackupHelper;
+import android.app.wallpaper.WallpaperDescription;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.pm.PackageManager;
@@ -194,9 +197,16 @@
 
         if (loadSystem) {
             if (!success) {
+                // Set safe values that won't cause crashes
                 wallpaper.cropHint.set(0, 0, 0, 0);
                 wpdData.mPadding.set(0, 0, 0, 0);
                 wallpaper.name = "";
+                if (liveWallpaperContentHandling()) {
+                    wallpaper.setDescription(new WallpaperDescription.Builder().setComponent(
+                            mImageWallpaper).build());
+                } else {
+                    wallpaper.setComponent(mImageWallpaper);
+                }
             } else {
                 if (wallpaper.wallpaperId <= 0) {
                     wallpaper.wallpaperId = makeWallpaperIdLocked();
@@ -245,25 +255,11 @@
                         parseWallpaperAttributes(parser, wallpaperToParse, keepDimensionHints);
                     }
 
-                    String comp = parser.getAttributeValue(null, "component");
+                    ComponentName comp = parseComponentName(parser);
                     if (removeNextWallpaperComponent()) {
-                        wallpaperToParse.setComponent(comp != null
-                                ? ComponentName.unflattenFromString(comp)
-                                : null);
-                        if (wallpaperToParse.getComponent() == null
-                                || "android".equals(wallpaperToParse.getComponent()
-                                .getPackageName())) {
-                            wallpaperToParse.setComponent(mImageWallpaper);
-                        }
+                        wallpaperToParse.setComponent(comp);
                     } else {
-                        wallpaperToParse.nextWallpaperComponent = comp != null
-                                ? ComponentName.unflattenFromString(comp)
-                                : null;
-                        if (wallpaperToParse.nextWallpaperComponent == null
-                                || "android".equals(wallpaperToParse.nextWallpaperComponent
-                                .getPackageName())) {
-                            wallpaperToParse.nextWallpaperComponent = mImageWallpaper;
-                        }
+                        wallpaperToParse.nextWallpaperComponent = comp;
                     }
 
                     if (multiCrop()) {
@@ -290,6 +286,17 @@
         return lockWallpaper;
     }
 
+    @NonNull
+    private ComponentName parseComponentName(TypedXmlPullParser parser) {
+        String comp = parser.getAttributeValue(null, "component");
+        ComponentName c = (comp != null) ? ComponentName.unflattenFromString(comp) : null;
+        if (c == null || "android".equals(c.getPackageName())) {
+            c = mImageWallpaper;
+        }
+
+        return c;
+    }
+
     private void ensureSaneWallpaperData(WallpaperData wallpaper) {
         // Only overwrite cropHint if the rectangle is invalid.
         if (wallpaper.cropHint.width() < 0
@@ -332,9 +339,29 @@
         }
     }
 
+    void parseWallpaperDescription(TypedXmlPullParser parser, WallpaperData wallpaper)
+            throws XmlPullParserException, IOException {
+
+        int type = parser.next();
+        if (type == XmlPullParser.START_TAG && "description".equals(parser.getName())) {
+            // Always read the description if it's there - there may be one from a previous save
+            // with content handling enabled even if it's enabled now
+            WallpaperDescription description = WallpaperDescription.restoreFromXml(parser);
+            if (liveWallpaperContentHandling()) {
+                // null component means that wallpaper was last saved without content handling, so
+                // populate description from saved component
+                if (description.getComponent() == null) {
+                    description = description.toBuilder().setComponent(
+                            parseComponentName(parser)).build();
+                }
+                wallpaper.setDescription(description);
+            }
+        }
+    }
+
     @VisibleForTesting
     void parseWallpaperAttributes(TypedXmlPullParser parser, WallpaperData wallpaper,
-            boolean keepDimensionHints) throws XmlPullParserException {
+            boolean keepDimensionHints) throws XmlPullParserException, IOException {
         final int id = parser.getAttributeInt(null, "id", -1);
         if (id != -1) {
             wallpaper.wallpaperId = id;
@@ -355,8 +382,7 @@
                 getAttributeInt(parser, "totalCropTop", 0),
                 getAttributeInt(parser, "totalCropRight", 0),
                 getAttributeInt(parser, "totalCropBottom", 0));
-        ComponentName componentName = removeNextWallpaperComponent() ? wallpaper.getComponent()
-                : wallpaper.nextWallpaperComponent;
+        ComponentName componentName = parseComponentName(parser);
         if (multiCrop() && mImageWallpaper.equals(componentName)) {
             wallpaper.mCropHints = new SparseArray<>();
             for (Pair<Integer, String> pair: screenDimensionPairs()) {
@@ -443,6 +469,15 @@
         }
         wallpaper.name = parser.getAttributeValue(null, "name");
         wallpaper.allowBackup = parser.getAttributeBoolean(null, "backup", false);
+
+        parseWallpaperDescription(parser, wallpaper);
+        if (liveWallpaperContentHandling() && wallpaper.getDescription().getComponent() == null) {
+            // The last save was done before the content handling flag was enabled and has no
+            // WallpaperDescription, so create a default one with the correct component.
+            // CSP: log boot after flag change to false -> true
+            wallpaper.setDescription(
+                    new WallpaperDescription.Builder().setComponent(componentName).build());
+        }
     }
 
     private static int getAttributeInt(TypedXmlPullParser parser, String name, int defValue) {
@@ -610,9 +645,27 @@
             out.attributeBoolean(null, "backup", true);
         }
 
+        writeWallpaperDescription(out, wallpaper);
+
         out.endTag(null, tag);
     }
 
+    void writeWallpaperDescription(TypedXmlSerializer out, WallpaperData wallpaper)
+            throws IOException {
+        if (liveWallpaperContentHandling()) {
+            WallpaperDescription description = wallpaper.getDescription();
+            if (description != null) {
+                String descriptionTag = "description";
+                out.startTag(null, descriptionTag);
+                try {
+                    description.saveToXml(out);
+                } catch (XmlPullParserException e) {
+                    Slog.e(TAG, "Error writing wallpaper description", e);
+                }
+                out.endTag(null, descriptionTag);
+            }
+        }
+    }
     // Restore the named resource bitmap to both source + crop files
     boolean restoreNamedResourceLocked(WallpaperData wallpaper) {
         if (wallpaper.name.length() > 4 && "res:".equals(wallpaper.name.substring(0, 4))) {
diff --git a/services/core/java/com/android/server/wearable/RemoteWearableSensingService.java b/services/core/java/com/android/server/wearable/RemoteWearableSensingService.java
index 6776f26..e697d15 100644
--- a/services/core/java/com/android/server/wearable/RemoteWearableSensingService.java
+++ b/services/core/java/com/android/server/wearable/RemoteWearableSensingService.java
@@ -19,7 +19,6 @@
 import static android.content.Context.BIND_FOREGROUND_SERVICE;
 import static android.content.Context.BIND_INCLUDE_CAPABILITIES;
 
-import android.app.wearable.Flags;
 import android.app.wearable.IWearableSensingCallback;
 import android.app.wearable.WearableSensingManager;
 import android.content.ComponentName;
@@ -42,7 +41,7 @@
 final class RemoteWearableSensingService extends ServiceConnector.Impl<IWearableSensingService> {
     private static final String TAG =
             com.android.server.wearable.RemoteWearableSensingService.class.getSimpleName();
-    private final static boolean DEBUG = false;
+    private static final boolean DEBUG = false;
 
     private final Object mSecureConnectionLock = new Object();
 
@@ -55,11 +54,12 @@
     @GuardedBy("mSecureConnectionLock")
     private boolean mSecureConnectionProvided = false;
 
-    RemoteWearableSensingService(Context context, ComponentName serviceName,
-            int userId) {
-        super(context, new Intent(
-                        WearableSensingService.SERVICE_INTERFACE).setComponent(serviceName),
-                BIND_FOREGROUND_SERVICE | BIND_INCLUDE_CAPABILITIES, userId,
+    RemoteWearableSensingService(Context context, ComponentName serviceName, int userId) {
+        super(
+                context,
+                new Intent(WearableSensingService.SERVICE_INTERFACE).setComponent(serviceName),
+                BIND_FOREGROUND_SERVICE | BIND_INCLUDE_CAPABILITIES,
+                userId,
                 IWearableSensingService.Stub::asInterface);
 
         // Bind right away
@@ -87,15 +87,6 @@
         if (DEBUG) {
             Slog.i(TAG, "#provideSecureConnection");
         }
-        if (!Flags.enableRestartWssProcess()) {
-            Slog.d(
-                    TAG,
-                    "FLAG_ENABLE_RESTART_WSS_PROCESS is disabled. Do not attempt to restart the"
-                        + " WearableSensingService process");
-            provideSecureConnectionInternal(
-                    secureWearableConnection, wearableSensingCallback, statusCallback);
-            return;
-        }
         synchronized (mSecureConnectionLock) {
             if (mNextSecureConnectionContext != null) {
                 // A process restart is in progress, #binderDied is about to be called. Replace
@@ -103,13 +94,11 @@
                 Slog.i(
                         TAG,
                         "A new wearable connection is provided before the process restart triggered"
-                            + " by the previous connection is complete. Discarding the previous"
-                            + " connection.");
-                if (Flags.enableProvideWearableConnectionApi()) {
-                    WearableSensingManagerPerUserService.notifyStatusCallback(
-                            mNextSecureConnectionContext.mStatusCallback,
-                            WearableSensingManager.STATUS_CHANNEL_ERROR);
-                }
+                                + " by the previous connection is complete. Discarding the previous"
+                                + " connection.");
+                WearableSensingManagerPerUserService.notifyStatusCallback(
+                        mNextSecureConnectionContext.mStatusCallback,
+                        WearableSensingManager.STATUS_CHANNEL_ERROR);
                 mNextSecureConnectionContext =
                         new SecureWearableConnectionContext(
                                 secureWearableConnection, wearableSensingCallback, statusCallback);
@@ -130,6 +119,32 @@
         }
     }
 
+    public void provideConcurrentSecureConnection(
+            ParcelFileDescriptor secureWearableConnection,
+            PersistableBundle metadata,
+            IWearableSensingCallback wearableSensingCallback,
+            RemoteCallback statusCallback) {
+        if (DEBUG) {
+            Slog.i(TAG, "#provideConcurrentSecureConnection");
+        }
+        var unused =
+                post(
+                        service -> {
+                            service.provideConcurrentSecureConnection(
+                                    secureWearableConnection,
+                                    metadata,
+                                    wearableSensingCallback,
+                                    statusCallback);
+                            try {
+                                // close the local fd after it has been sent to the
+                                // WearableSensingService process
+                                secureWearableConnection.close();
+                            } catch (IOException ex) {
+                                Slog.w(TAG, "Unable to close the local parcelFileDescriptor.", ex);
+                            }
+                        });
+    }
+
     private void provideSecureConnectionInternal(
             ParcelFileDescriptor secureWearableConnection,
             IWearableSensingCallback wearableSensingCallback,
@@ -174,6 +189,28 @@
         var unused = post(service -> service.killProcess());
     }
 
+    /** Provides a read-only {@link ParcelFileDescriptor} to the WearableSensingService. */
+    public void provideReadOnlyParcelFileDescriptor(
+            ParcelFileDescriptor parcelFileDescriptor,
+            PersistableBundle metadata,
+            RemoteCallback callback) {
+        if (DEBUG) {
+            Slog.i(TAG, "Providing read-only ParcelFileDescriptor.");
+        }
+        var unused =
+                post(
+                        service -> {
+                            service.provideReadOnlyParcelFileDescriptor(
+                                    parcelFileDescriptor, metadata, callback);
+                            try {
+                                // close the local fd after it has been sent to the WSS process
+                                parcelFileDescriptor.close();
+                            } catch (IOException ex) {
+                                Slog.w(TAG, "Unable to close the local parcelFileDescriptor.", ex);
+                            }
+                        });
+    }
+
     /**
      * Provides the implementation a data stream to the wearable.
      *
@@ -210,9 +247,8 @@
      * @param sharedMemory The unrestricted data blob to provide to the implementation.
      * @param callback The callback for service status
      */
-    public void provideData(PersistableBundle data,
-            SharedMemory sharedMemory,
-            RemoteCallback callback) {
+    public void provideData(
+            PersistableBundle data, SharedMemory sharedMemory, RemoteCallback callback) {
         if (DEBUG) {
             Slog.i(TAG, "Providing data.");
         }
diff --git a/services/core/java/com/android/server/wearable/WearableSensingManagerPerUserService.java b/services/core/java/com/android/server/wearable/WearableSensingManagerPerUserService.java
index 36e5200..3958169 100644
--- a/services/core/java/com/android/server/wearable/WearableSensingManagerPerUserService.java
+++ b/services/core/java/com/android/server/wearable/WearableSensingManagerPerUserService.java
@@ -51,6 +51,7 @@
 import android.util.IndentingPrintWriter;
 import android.util.Slog;
 
+import com.android.internal.R;
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.infra.AndroidFuture;
@@ -59,21 +60,22 @@
 
 import java.io.IOException;
 import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicInteger;
 import java.util.concurrent.atomic.AtomicReference;
 
-/**
- * Per-user manager service for managing sensing {@link AmbientContextEvent}s on Wearables.
- */
-final class WearableSensingManagerPerUserService extends
-        AbstractPerUserSystemService<WearableSensingManagerPerUserService,
-                WearableSensingManagerService> {
+/** Per-user manager service for managing sensing {@link AmbientContextEvent}s on Wearables. */
+final class WearableSensingManagerPerUserService
+        extends AbstractPerUserSystemService<
+                WearableSensingManagerPerUserService, WearableSensingManagerService> {
     private static final String TAG = WearableSensingManagerPerUserService.class.getSimpleName();
 
     private final PackageManagerInternal mPackageManagerInternal;
 
-    @Nullable
-    @VisibleForTesting
-    RemoteWearableSensingService mRemoteService;
+    @Nullable @VisibleForTesting RemoteWearableSensingService mRemoteService;
 
     @Nullable private VoiceInteractionManagerInternal mVoiceInteractionManagerInternal;
 
@@ -85,16 +87,32 @@
     @GuardedBy("mSecureChannelLock")
     private WearableSensingSecureChannel mSecureChannel;
 
+    // mSecureChannelMap is used by the WearableSensingManager#provideConnection(
+    // WearableConnection, Executor) API, which allows up to mMaxNumberOfConcurrentConnections
+    // concurrent connections, while the mSecureChannel above is used by the deprecated
+    // #provideConnection(ParcelFileDescriptor, Executor, Consumer) API, which does not allow
+    // concurrent connections.
+    @GuardedBy("mSecureChannelMap")
+    private final Map<Integer, WearableSensingSecureChannel> mSecureChannelMap = new HashMap<>();
+
+    private final AtomicInteger mNextConnectionId = new AtomicInteger(1);
+
+    private final int mMaxNumberOfConcurrentConnections;
+
     WearableSensingManagerPerUserService(
             @NonNull WearableSensingManagerService master, Object lock, @UserIdInt int userId) {
         super(master, lock, userId);
         mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
+        mMaxNumberOfConcurrentConnections =
+                getContext()
+                        .getResources()
+                        .getInteger(
+                                R.integer.config_maxWearableSensingServiceConcurrentConnections);
     }
 
     public static void notifyStatusCallback(RemoteCallback statusCallback, int statusCode) {
         Bundle bundle = new Bundle();
-        bundle.putInt(
-                WearableSensingManager.STATUS_RESPONSE_BUNDLE_KEY, statusCode);
+        bundle.putInt(WearableSensingManager.STATUS_RESPONSE_BUNDLE_KEY, statusCode);
         statusCallback.sendResult(bundle);
     }
 
@@ -116,8 +134,8 @@
     @GuardedBy("mLock")
     private void ensureRemoteServiceInitiated() {
         if (mRemoteService == null) {
-            mRemoteService = new RemoteWearableSensingService(
-                    getContext(), mComponentName, getUserId());
+            mRemoteService =
+                    new RemoteWearableSensingService(getContext(), mComponentName, getUserId());
         }
     }
 
@@ -130,18 +148,15 @@
         return mVoiceInteractionManagerInternal != null;
     }
 
-    /**
-     * get the currently bound component name.
-     */
+    /** get the currently bound component name. */
     @VisibleForTesting
     ComponentName getComponentName() {
         return mComponentName;
     }
 
-
     /**
-     * Resolves and sets up the service if it had not been done yet. Returns true if the service
-     * is available.
+     * Resolves and sets up the service if it had not been done yet. Returns true if the service is
+     * available.
      */
     @GuardedBy("mLock")
     @VisibleForTesting
@@ -155,8 +170,7 @@
 
         ServiceInfo serviceInfo;
         try {
-            serviceInfo = AppGlobals.getPackageManager().getServiceInfo(
-                    mComponentName, 0, mUserId);
+            serviceInfo = AppGlobals.getPackageManager().getServiceInfo(mComponentName, 0, mUserId);
         } catch (RemoteException e) {
             Slog.w(TAG, "RemoteException while setting up service");
             return false;
@@ -169,17 +183,17 @@
             throws PackageManager.NameNotFoundException {
         ServiceInfo serviceInfo;
         try {
-            serviceInfo = AppGlobals.getPackageManager().getServiceInfo(serviceComponent,
-                    0, mUserId);
+            serviceInfo =
+                    AppGlobals.getPackageManager().getServiceInfo(serviceComponent, 0, mUserId);
             if (serviceInfo != null) {
                 final String permission = serviceInfo.permission;
-                if (!Manifest.permission.BIND_WEARABLE_SENSING_SERVICE.equals(
-                        permission)) {
-                    throw new SecurityException(String.format(
-                            "Service %s requires %s permission. Found %s permission",
-                            serviceInfo.getComponentName(),
-                            Manifest.permission.BIND_WEARABLE_SENSING_SERVICE,
-                            serviceInfo.permission));
+                if (!Manifest.permission.BIND_WEARABLE_SENSING_SERVICE.equals(permission)) {
+                    throw new SecurityException(
+                            String.format(
+                                    "Service %s requires %s permission. Found %s permission",
+                                    serviceInfo.getComponentName(),
+                                    Manifest.permission.BIND_WEARABLE_SENSING_SERVICE,
+                                    serviceInfo.permission));
                 }
             }
         } catch (RemoteException e) {
@@ -199,6 +213,20 @@
         }
     }
 
+    /** Returns the number of available concurrent connection quota. */
+    public int getAvailableConnectionCount() {
+        synchronized (mSecureChannelMap) {
+            if (mSecureChannelMap.size() > mMaxNumberOfConcurrentConnections) {
+                Slog.e(
+                        TAG,
+                        "mMaxNumberOfConcurrentConnections exceeded. This should not be"
+                                + " possible!");
+                return 0;
+            }
+            return mMaxNumberOfConcurrentConnections - mSecureChannelMap.size();
+        }
+    }
+
     /**
      * Creates a CompanionDeviceManager secure channel and sends a proxy to the wearable sensing
      * service.
@@ -245,36 +273,198 @@
 
                                     @Override
                                     public void onError() {
-                                        if (Flags.enableRestartWssProcess()) {
-                                            synchronized (mSecureChannelLock) {
-                                                if (mSecureChannel != null
-                                                        && mSecureChannel
-                                                                == currentSecureChannelRef.get()) {
-                                                    mRemoteService
-                                                            .killWearableSensingServiceProcess();
-                                                    mSecureChannel = null;
-                                                }
+                                        synchronized (mSecureChannelLock) {
+                                            if (mSecureChannel != null
+                                                    && mSecureChannel
+                                                            == currentSecureChannelRef.get()) {
+                                                mRemoteService.killWearableSensingServiceProcess();
+                                                mSecureChannel = null;
                                             }
                                         }
-                                        if (Flags.enableProvideWearableConnectionApi()) {
-                                            notifyStatusCallback(
-                                                    statusCallback,
-                                                    WearableSensingManager.STATUS_CHANNEL_ERROR);
-                                        }
+                                        notifyStatusCallback(
+                                                statusCallback,
+                                                WearableSensingManager.STATUS_CHANNEL_ERROR);
                                     }
                                 });
                 currentSecureChannelRef.set(mSecureChannel);
             } catch (IOException ex) {
                 Slog.e(TAG, "Unable to create the secure channel.", ex);
-                if (Flags.enableProvideWearableConnectionApi()) {
-                    notifyStatusCallback(
-                            statusCallback, WearableSensingManager.STATUS_CHANNEL_ERROR);
-                }
+                notifyStatusCallback(statusCallback, WearableSensingManager.STATUS_CHANNEL_ERROR);
             }
         }
     }
 
     /**
+     * Creates a CompanionDeviceManager secure channel and sends a proxy to the wearable sensing
+     * service.
+     */
+    public int onProvideConcurrentConnection(
+            ParcelFileDescriptor wearableConnection,
+            PersistableBundle metadata,
+            IWearableSensingCallback wearableSensingCallback,
+            RemoteCallback statusCallback) {
+        Slog.i(TAG, "onProvideConcurrentConnection in per user service.");
+        synchronized (mLock) {
+            if (!setUpServiceIfNeeded()) {
+                Slog.w(TAG, "Detection service is not available at this moment.");
+                notifyStatusCallback(
+                        statusCallback, WearableSensingManager.STATUS_SERVICE_UNAVAILABLE);
+                return WearableSensingManager.CONNECTION_ID_INVALID;
+            }
+        }
+        boolean isConcurrentConnectionLimitReached = false;
+        synchronized (mSecureChannelMap) {
+            // Do a pre-check on concurrent connection count. We need another check right before we
+            // add the connection into the map to prevent race conditions, but that can only happen
+            // after the WearableSensingSecureChannel is created. This check here allows us to
+            // reject before creating a new secure channel.
+            if (mSecureChannelMap.size() >= mMaxNumberOfConcurrentConnections) {
+                isConcurrentConnectionLimitReached = true;
+            }
+        }
+        if (isConcurrentConnectionLimitReached) {
+            Slog.i(
+                    TAG,
+                    "Rejecting connection because max concurrent connections limit has been"
+                            + " reached.");
+            if (Flags.enableConcurrentWearableConnections()) {
+                notifyStatusCallback(
+                        statusCallback,
+                        WearableSensingManager.STATUS_MAX_CONCURRENT_CONNECTIONS_EXCEEDED);
+            }
+            return WearableSensingManager.CONNECTION_ID_INVALID;
+        }
+        int connectionId = mNextConnectionId.getAndIncrement();
+        RemoteCallback wrappedStatusCallback =
+                wrapCallbackWithSecureChannelMapCleanUp(statusCallback, connectionId);
+        WearableSensingSecureChannel secureChannel;
+        try {
+            secureChannel =
+                    WearableSensingSecureChannel.create(
+                            getContext().getSystemService(CompanionDeviceManager.class),
+                            wearableConnection,
+                            new WearableSensingSecureChannel.SecureTransportListener() {
+                                @Override
+                                public void onSecureTransportAvailable(
+                                        ParcelFileDescriptor secureTransport) {
+                                    Slog.i(TAG, "calling over to remote service.");
+                                    synchronized (mLock) {
+                                        ensureRemoteServiceInitiated();
+                                        mRemoteService.provideConcurrentSecureConnection(
+                                                secureTransport,
+                                                metadata,
+                                                wearableSensingCallback,
+                                                wrappedStatusCallback);
+                                    }
+                                }
+
+                                @Override
+                                public void onError() {
+                                    synchronized (mSecureChannelMap) {
+                                        mSecureChannelMap.remove(connectionId);
+                                    }
+                                    notifyStatusCallback(
+                                            wrappedStatusCallback,
+                                            WearableSensingManager.STATUS_CHANNEL_ERROR);
+                                }
+                            });
+        } catch (IOException ex) {
+            Slog.e(TAG, "Unable to create the secure channel.", ex);
+            notifyStatusCallback(statusCallback, WearableSensingManager.STATUS_CHANNEL_ERROR);
+            return WearableSensingManager.CONNECTION_ID_INVALID;
+        }
+        synchronized (mSecureChannelMap) {
+            if (mSecureChannelMap.size() >= mMaxNumberOfConcurrentConnections) {
+                isConcurrentConnectionLimitReached = true;
+            } else {
+                mSecureChannelMap.put(connectionId, secureChannel);
+            }
+        }
+        if (isConcurrentConnectionLimitReached) {
+            Slog.i(
+                    TAG,
+                    "Rejecting connection because max concurrent connections limit has been"
+                            + " reached.");
+            if (Flags.enableConcurrentWearableConnections()) {
+                notifyStatusCallback(
+                        statusCallback,
+                        WearableSensingManager.STATUS_MAX_CONCURRENT_CONNECTIONS_EXCEEDED);
+            }
+            secureChannel.close();
+            return WearableSensingManager.CONNECTION_ID_INVALID;
+        }
+        return connectionId;
+    }
+
+    private RemoteCallback wrapCallbackWithSecureChannelMapCleanUp(
+            RemoteCallback statusCallback, int connectionId) {
+        return new RemoteCallback(
+                result -> {
+                    int status = result.getInt(WearableSensingManager.STATUS_RESPONSE_BUNDLE_KEY);
+                    if (status != WearableSensingManager.STATUS_SUCCESS) {
+                        removeConnection(connectionId);
+                    }
+                    statusCallback.sendResult(result);
+                });
+    }
+
+    /**
+     * Removes a connection by ID.
+     *
+     * @param connectionId The ID of the connection to remove.
+     * @return true if the {@code connectionId} corresponds to a stored connection. Returns false
+     *     otherwise.
+     */
+    public boolean removeConnection(int connectionId) {
+        WearableSensingSecureChannel removedChannel;
+        synchronized (mSecureChannelMap) {
+            removedChannel = mSecureChannelMap.remove(connectionId);
+        }
+        if (removedChannel != null) {
+            removedChannel.close();
+            return true;
+        }
+        return false;
+    }
+
+    /** Removes all stored connections. */
+    public void removeAllConnections() {
+        List<WearableSensingSecureChannel> allChannels;
+        synchronized (mSecureChannelMap) {
+            allChannels = new ArrayList<>(mSecureChannelMap.values());
+            mSecureChannelMap.clear();
+        }
+        for (WearableSensingSecureChannel channel : allChannels) {
+            channel.close();
+        }
+    }
+
+    /**
+     * Handles sending the provided read-only {@link ParcelFileDescriptor} to the wearable sensing
+     * service.
+     */
+    public void onProvideReadOnlyParcelFileDescriptor(
+            ParcelFileDescriptor parcelFileDescriptor,
+            PersistableBundle metadata,
+            RemoteCallback statusCallback) {
+        if (!isReadOnly(parcelFileDescriptor)) {
+            throw new IllegalArgumentException("Provided ParcelFileDescriptor is not read-only.");
+        }
+        synchronized (mLock) {
+            if (!setUpServiceIfNeeded()) {
+                Slog.w(TAG, "Detection service is not available at this moment.");
+                notifyStatusCallback(
+                        statusCallback, WearableSensingManager.STATUS_SERVICE_UNAVAILABLE);
+                return;
+            }
+            Slog.i(TAG, "calling over to remote servvice.");
+            ensureRemoteServiceInitiated();
+            mRemoteService.provideReadOnlyParcelFileDescriptor(
+                    parcelFileDescriptor, metadata, statusCallback);
+        }
+    }
+
+    /**
      * Handles sending the provided data stream for the wearable to the wearable sensing service.
      */
     public void onProvideDataStream(
@@ -301,12 +491,9 @@
         }
     }
 
-    /**
-     * Handles sending the provided data to the wearable sensing service.
-     */
-    public void onProvidedData(PersistableBundle data,
-            SharedMemory sharedMemory,
-            RemoteCallback callback) {
+    /** Handles sending the provided data to the wearable sensing service. */
+    public void onProvidedData(
+            PersistableBundle data, SharedMemory sharedMemory, RemoteCallback callback) {
         synchronized (mLock) {
             if (!setUpServiceIfNeeded()) {
                 Slog.w(TAG, "Detection service is not available at this moment.");
@@ -557,7 +744,7 @@
             Slog.w(
                     TAG,
                     "Error encountered when trying to determine if the parcelFileDescriptor is"
-                        + " read-only. Treating it as not read-only",
+                            + " read-only. Treating it as not read-only",
                     ex);
         }
         return false;
diff --git a/services/core/java/com/android/server/wearable/WearableSensingManagerService.java b/services/core/java/com/android/server/wearable/WearableSensingManagerService.java
index ac419a5..7297a23 100644
--- a/services/core/java/com/android/server/wearable/WearableSensingManagerService.java
+++ b/services/core/java/com/android/server/wearable/WearableSensingManagerService.java
@@ -66,11 +66,11 @@
  * System service for managing sensing {@link AmbientContextEvent}s on Wearables.
  *
  * <p>The use of "Wearable" here is not the same as the Android Wear platform and should be treated
- * separately. </p>
+ * separately.
  */
-public class WearableSensingManagerService extends
-        AbstractMasterSystemService<WearableSensingManagerService,
-                WearableSensingManagerPerUserService> {
+public class WearableSensingManagerService
+        extends AbstractMasterSystemService<
+                WearableSensingManagerService, WearableSensingManagerPerUserService> {
     private static final String TAG = WearableSensingManagerService.class.getSimpleName();
     private static final String KEY_SERVICE_ENABLED = "service_enabled";
 
@@ -111,11 +111,11 @@
     volatile boolean mIsServiceEnabled;
 
     public WearableSensingManagerService(Context context) {
-        super(context,
+        super(
+                context,
                 new FrameworkResourcesServiceNameResolver(
-                        context,
-                        R.string.config_defaultWearableSensingService),
-                /*disallowProperty=*/null,
+                        context, R.string.config_defaultWearableSensingService),
+                /* disallowProperty= */ null,
                 PACKAGE_UPDATE_POLICY_REFRESH_EAGER
                         | /*To avoid high latency*/ PACKAGE_RESTART_POLICY_REFRESH_EAGER);
         mContext = context;
@@ -141,24 +141,27 @@
                     getContext().getMainExecutor(),
                     (properties) -> onDeviceConfigChange(properties.getKeyset()));
 
-            mIsServiceEnabled = DeviceConfig.getBoolean(
-                    NAMESPACE_WEARABLE_SENSING,
-                    KEY_SERVICE_ENABLED, DEFAULT_SERVICE_ENABLED);
+            mIsServiceEnabled =
+                    DeviceConfig.getBoolean(
+                            NAMESPACE_WEARABLE_SENSING,
+                            KEY_SERVICE_ENABLED,
+                            DEFAULT_SERVICE_ENABLED);
         }
     }
 
-
     private void onDeviceConfigChange(@NonNull Set<String> keys) {
         if (keys.contains(KEY_SERVICE_ENABLED)) {
-            mIsServiceEnabled = DeviceConfig.getBoolean(
-                    NAMESPACE_WEARABLE_SENSING,
-                    KEY_SERVICE_ENABLED, DEFAULT_SERVICE_ENABLED);
+            mIsServiceEnabled =
+                    DeviceConfig.getBoolean(
+                            NAMESPACE_WEARABLE_SENSING,
+                            KEY_SERVICE_ENABLED,
+                            DEFAULT_SERVICE_ENABLED);
         }
     }
 
     @Override
-    protected WearableSensingManagerPerUserService newServiceLocked(int resolvedUserId,
-            boolean disabled) {
+    protected WearableSensingManagerPerUserService newServiceLocked(
+            int resolvedUserId, boolean disabled) {
         return new WearableSensingManagerPerUserService(this, mLock, resolvedUserId);
     }
 
@@ -181,8 +184,8 @@
 
     @Override
     protected void enforceCallingPermissionForManagement() {
-        getContext().enforceCallingPermission(
-                Manifest.permission.ACCESS_AMBIENT_CONTEXT_EVENT, TAG);
+        getContext()
+                .enforceCallingPermission(Manifest.permission.ACCESS_AMBIENT_CONTEXT_EVENT, TAG);
     }
 
     @Override
@@ -193,9 +196,7 @@
         return MAX_TEMPORARY_SERVICE_DURATION_MS;
     }
 
-    /**
-     * Returns the AmbientContextManagerPerUserService component for this user.
-     */
+    /** Returns the AmbientContextManagerPerUserService component for this user. */
     public ComponentName getComponentName(@UserIdInt int userId) {
         synchronized (mLock) {
             final WearableSensingManagerPerUserService service = getServiceForUserLocked(userId);
@@ -260,8 +261,8 @@
      *
      * <p>The current rate limit will also be reset.
      *
-     * <p>This method is only used for testing and must not be called in production code because
-     * it effectively bypasses the rate limiting introduced to enhance privacy protection.
+     * <p>This method is only used for testing and must not be called in production code because it
+     * effectively bypasses the rate limiting introduced to enhance privacy protection.
      */
     @VisibleForTesting
     void setDataRequestRateLimitWindowSize(@NonNull Duration windowSize) {
@@ -269,7 +270,7 @@
                 TAG,
                 TextUtils.formatSimple(
                         "Setting the data request rate limit window size to %s. This also resets"
-                            + " the current limit and should only be callable from a test.",
+                                + " the current limit and should only be callable from a test.",
                         windowSize));
         mDataRequestRateLimiter =
                 new MultiRateLimiter.Builder(mContext)
@@ -282,8 +283,8 @@
      *
      * <p>The current rate limit will also be reset.
      *
-     * <p>This method is only used for testing and must not be called in production code because
-     * it effectively bypasses the rate limiting introduced to enhance privacy protection.
+     * <p>This method is only used for testing and must not be called in production code because it
+     * effectively bypasses the rate limiting introduced to enhance privacy protection.
      */
     @VisibleForTesting
     void resetDataRequestRateLimitWindowSize() {
@@ -391,14 +392,16 @@
 
     private void callPerUserServiceIfExist(
             Consumer<WearableSensingManagerPerUserService> serviceConsumer,
-            RemoteCallback statusCallback) {
+            @Nullable RemoteCallback statusCallback) {
         int userId = UserHandle.getCallingUserId();
         synchronized (mLock) {
             WearableSensingManagerPerUserService service = getServiceForUserLocked(userId);
             if (service == null) {
                 Slog.w(TAG, "Service not available for userId " + userId);
-                WearableSensingManagerPerUserService.notifyStatusCallback(statusCallback,
-                        WearableSensingManager.STATUS_SERVICE_UNAVAILABLE);
+                if (statusCallback != null) {
+                    WearableSensingManagerPerUserService.notifyStatusCallback(
+                            statusCallback, WearableSensingManager.STATUS_SERVICE_UNAVAILABLE);
+                }
                 return;
             }
             serviceConsumer.accept(service);
@@ -408,6 +411,16 @@
     private final class WearableSensingManagerInternal extends IWearableSensingManager.Stub {
 
         @Override
+        public int getAvailableConnectionCount() {
+            WearableSensingManagerPerUserService service =
+                    validateAndGetPerUserService(/* statusCallback= */ null);
+            if (service == null) {
+                return 0;
+            }
+            return service.getAvailableConnectionCount();
+        }
+
+        @Override
         public void provideConnection(
                 ParcelFileDescriptor wearableConnection,
                 IWearableSensingCallback wearableSensingCallback,
@@ -431,6 +444,63 @@
         }
 
         @Override
+        public int provideConcurrentConnection(
+                ParcelFileDescriptor wearableConnection,
+                PersistableBundle metadata,
+                IWearableSensingCallback wearableSensingCallback,
+                RemoteCallback statusCallback) {
+            Slog.i(TAG, "WearableSensingManagerInternal provideConcurrentConnection.");
+            Objects.requireNonNull(wearableConnection);
+            Objects.requireNonNull(metadata);
+            Objects.requireNonNull(wearableSensingCallback);
+            Objects.requireNonNull(statusCallback);
+            WearableSensingManagerPerUserService service =
+                    validateAndGetPerUserService(statusCallback);
+            if (service == null) {
+                return WearableSensingManager.CONNECTION_ID_INVALID;
+            }
+            return service.onProvideConcurrentConnection(
+                    wearableConnection, metadata, wearableSensingCallback, statusCallback);
+        }
+
+        @Override
+        public boolean removeConnection(int connectionId) {
+            Slog.i(TAG, "WearableSensingManagerInternal removeConnection.");
+            WearableSensingManagerPerUserService service =
+                    validateAndGetPerUserService(/* statusCallback= */ null);
+            if (service == null) {
+                return false;
+            }
+            return service.removeConnection(connectionId);
+        }
+
+        @Override
+        public void removeAllConnections() {
+            Slog.i(TAG, "WearableSensingManagerInternal removeAllConnections.");
+            WearableSensingManagerPerUserService service =
+                    validateAndGetPerUserService(/* statusCallback= */ null);
+            if (service == null) {
+                return;
+            }
+            service.removeAllConnections();
+        }
+
+        @Override
+        public void provideReadOnlyParcelFileDescriptor(
+                ParcelFileDescriptor parcelFileDescriptor,
+                PersistableBundle metadata,
+                RemoteCallback statusCallback) {
+            Slog.i(TAG, "WearableSensingManagerInternal provideReadOnlyParcelFileDescriptor.");
+            WearableSensingManagerPerUserService service =
+                    validateAndGetPerUserService(statusCallback);
+            if (service == null) {
+                return;
+            }
+            service.onProvideReadOnlyParcelFileDescriptor(
+                    parcelFileDescriptor, metadata, statusCallback);
+        }
+
+        @Override
         public void provideDataStream(
                 ParcelFileDescriptor parcelFileDescriptor,
                 @Nullable IWearableSensingCallback wearableSensingCallback,
@@ -455,9 +525,7 @@
 
         @Override
         public void provideData(
-                PersistableBundle data,
-                SharedMemory sharedMemory,
-                RemoteCallback callback) {
+                PersistableBundle data, SharedMemory sharedMemory, RemoteCallback callback) {
             Slog.d(TAG, "WearableSensingManagerInternal provideData.");
             Objects.requireNonNull(data);
             Objects.requireNonNull(callback);
@@ -470,8 +538,7 @@
                 return;
             }
             callPerUserServiceIfExist(
-                    service -> service.onProvidedData(data, sharedMemory, callback),
-                    callback);
+                    service -> service.onProvidedData(data, sharedMemory, callback), callback);
         }
 
         @Override
@@ -601,10 +668,44 @@
         }
 
         @Override
-        public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
-                String[] args, ShellCallback callback, ResultReceiver resultReceiver) {
-            new WearableSensingShellCommand(WearableSensingManagerService.this).exec(
-                    this, in, out, err, args, callback, resultReceiver);
+        public void onShellCommand(
+                FileDescriptor in,
+                FileDescriptor out,
+                FileDescriptor err,
+                String[] args,
+                ShellCallback callback,
+                ResultReceiver resultReceiver) {
+            new WearableSensingShellCommand(WearableSensingManagerService.this)
+                    .exec(this, in, out, err, args, callback, resultReceiver);
+        }
+
+        @Nullable
+        private WearableSensingManagerPerUserService validateAndGetPerUserService(
+                @Nullable RemoteCallback statusCallback) {
+            mContext.enforceCallingOrSelfPermission(
+                    Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE, TAG);
+            if (!mIsServiceEnabled) {
+                Slog.w(TAG, "Service not available.");
+                if (statusCallback != null) {
+                    WearableSensingManagerPerUserService.notifyStatusCallback(
+                            statusCallback, WearableSensingManager.STATUS_SERVICE_UNAVAILABLE);
+                }
+                return null;
+            }
+            int userId = UserHandle.getCallingUserId();
+            WearableSensingManagerPerUserService service;
+            synchronized (mLock) {
+                service = getServiceForUserLocked(userId);
+            }
+            if (service == null) {
+                Slog.w(TAG, "Service not available for userId " + userId);
+                if (statusCallback != null) {
+                    WearableSensingManagerPerUserService.notifyStatusCallback(
+                            statusCallback, WearableSensingManager.STATUS_SERVICE_UNAVAILABLE);
+                }
+                return null;
+            }
+            return service;
         }
     }
 }
diff --git a/services/core/java/com/android/server/wm/ActivityClientController.java b/services/core/java/com/android/server/wm/ActivityClientController.java
index d119a08..05794cd 100644
--- a/services/core/java/com/android/server/wm/ActivityClientController.java
+++ b/services/core/java/com/android/server/wm/ActivityClientController.java
@@ -886,7 +886,10 @@
 
     @Override
     public boolean convertToTranslucent(IBinder token, Bundle options) {
-        final SafeActivityOptions safeOptions = SafeActivityOptions.fromBundle(options);
+        final int callingPid = Binder.getCallingPid();
+        final int callingUid = Binder.getCallingUid();
+        final SafeActivityOptions safeOptions = SafeActivityOptions.fromBundle(
+                options, callingPid, callingUid);
         final long origId = Binder.clearCallingIdentity();
         try {
             synchronized (mGlobalLock) {
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 25dfbd7..a71b10f 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -1792,8 +1792,6 @@
         }
         prevDc.onRunningActivityChanged();
 
-        // TODO(b/169035022): move to a more-appropriate place.
-        mTransitionController.collect(this);
         if (prevDc.mOpeningApps.remove(this)) {
             // Transfer opening transition to new display.
             mDisplayContent.mOpeningApps.add(this);
@@ -6461,7 +6459,7 @@
         returningOptions = null;
 
         if (canTurnScreenOn()) {
-            mTaskSupervisor.wakeUp("turnScreenOnFlag");
+            mTaskSupervisor.wakeUp(getDisplayId(), "turnScreenOnFlag");
         } else {
             // If the screen is going to turn on because the caller explicitly requested it and
             // the keyguard is not showing don't attempt to sleep. Otherwise the Activity will
diff --git a/services/core/java/com/android/server/wm/ActivityStartController.java b/services/core/java/com/android/server/wm/ActivityStartController.java
index a2c2dfc..6e97f8a 100644
--- a/services/core/java/com/android/server/wm/ActivityStartController.java
+++ b/services/core/java/com/android/server/wm/ActivityStartController.java
@@ -34,7 +34,6 @@
 import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.app.ActivityOptions;
-import android.app.BackgroundStartPrivileges;
 import android.app.IApplicationThread;
 import android.content.ComponentName;
 import android.content.ContentResolver;
@@ -191,7 +190,8 @@
                 .setOutActivity(tmpOutRecord)
                 .setCallingUid(0)
                 .setActivityInfo(aInfo)
-                .setActivityOptions(options.toBundle())
+                .setActivityOptions(options.toBundle(),
+                        Binder.getCallingPid(), Binder.getCallingUid())
                 .execute();
         mLastHomeActivityStartRecord = tmpOutRecord[0];
         if (rootHomeTask.mInResumeTopActivity) {
@@ -280,8 +280,8 @@
      * @param validateIncomingUser Set true to skip checking {@code userId} with the calling UID.
      * @param originatingPendingIntent PendingIntentRecord that originated this activity start or
      *        null if not originated by PendingIntent
-     * @param forcedBalByPiSender If set to allow, the
-     *        PendingIntent's sender will try to force allow background activity starts.
+     * @param allowBalExemptionForSystemProcess If set to {@code true}, the
+     *        PendingIntent's sender will allow additional exemptions.
      *        This is only possible if the sender of the PendingIntent is a system process.
      */
     final int startActivityInPackage(int uid, int realCallingPid, int realCallingUid,
@@ -289,7 +289,7 @@
             String resolvedType, IBinder resultTo, String resultWho, int requestCode,
             int startFlags, SafeActivityOptions options, int userId, Task inTask, String reason,
             boolean validateIncomingUser, PendingIntentRecord originatingPendingIntent,
-            BackgroundStartPrivileges forcedBalByPiSender) {
+            boolean allowBalExemptionForSystemProcess) {
 
         userId = checkTargetUser(userId, validateIncomingUser, realCallingPid, realCallingUid,
                 reason);
@@ -310,7 +310,7 @@
                 .setUserId(userId)
                 .setInTask(inTask)
                 .setOriginatingPendingIntent(originatingPendingIntent)
-                .setBackgroundStartPrivileges(forcedBalByPiSender)
+                .setAllowBalExemptionForSystemProcess(allowBalExemptionForSystemProcess)
                 .execute();
     }
 
@@ -325,18 +325,18 @@
      * @param validateIncomingUser Set true to skip checking {@code userId} with the calling UID.
      * @param originatingPendingIntent PendingIntentRecord that originated this activity start or
      *        null if not originated by PendingIntent
-     * @param forcedBalByPiSender If set to allow, the
-     *        PendingIntent's sender will try to force allow background activity starts.
+     * @param allowBalExemptionForSystemProcess If set to {@code true}, the
+     *        PendingIntent's sender will allow additional exemptions.
      *        This is only possible if the sender of the PendingIntent is a system process.
      */
     final int startActivitiesInPackage(int uid, String callingPackage,
             @Nullable String callingFeatureId, Intent[] intents, String[] resolvedTypes,
             IBinder resultTo, SafeActivityOptions options, int userId, boolean validateIncomingUser,
             PendingIntentRecord originatingPendingIntent,
-            BackgroundStartPrivileges forcedBalByPiSender) {
+            boolean allowBalExemptionForSystemProcess) {
         return startActivitiesInPackage(uid, 0 /* realCallingPid */, -1 /* realCallingUid */,
                 callingPackage, callingFeatureId, intents, resolvedTypes, resultTo, options, userId,
-                validateIncomingUser, originatingPendingIntent, forcedBalByPiSender);
+                validateIncomingUser, originatingPendingIntent, allowBalExemptionForSystemProcess);
     }
 
     /**
@@ -351,15 +351,15 @@
      * @param validateIncomingUser Set true to skip checking {@code userId} with the calling UID.
      * @param originatingPendingIntent PendingIntentRecord that originated this activity start or
      *        null if not originated by PendingIntent
-     * @param forcedBalByPiSender If set to allow, the
-     *        PendingIntent's sender will try to force allow background activity starts.
+     * @param allowBalExemptionForSystemProcess If set to {@code true}, the
+     *        PendingIntent's sender will allow additional exemptions.
      *        This is only possible if the sender of the PendingIntent is a system process.
      */
     final int startActivitiesInPackage(int uid, int realCallingPid, int realCallingUid,
             String callingPackage, @Nullable String callingFeatureId, Intent[] intents,
             String[] resolvedTypes, IBinder resultTo, SafeActivityOptions options, int userId,
             boolean validateIncomingUser, PendingIntentRecord originatingPendingIntent,
-            BackgroundStartPrivileges forcedBalByPiSender) {
+            boolean allowBalExemptionForSystemProcess) {
 
         final String reason = "startActivityInPackage";
 
@@ -369,14 +369,14 @@
         // TODO: Switch to user app stacks here.
         return startActivities(null, uid, realCallingPid, realCallingUid, callingPackage,
                 callingFeatureId, intents, resolvedTypes, resultTo, options, userId, reason,
-                originatingPendingIntent, forcedBalByPiSender);
+                originatingPendingIntent, allowBalExemptionForSystemProcess);
     }
 
     int startActivities(IApplicationThread caller, int callingUid, int incomingRealCallingPid,
             int incomingRealCallingUid, String callingPackage, @Nullable String callingFeatureId,
             Intent[] intents, String[] resolvedTypes, IBinder resultTo, SafeActivityOptions options,
             int userId, String reason, PendingIntentRecord originatingPendingIntent,
-            BackgroundStartPrivileges forcedBalByPiSender) {
+            boolean allowBalExemptionForSystemProcess) {
         if (intents == null) {
             throw new NullPointerException("intents is null");
         }
@@ -518,7 +518,7 @@
                         // top one as otherwise an activity below might consume it.
                         .setAllowPendingRemoteAnimationRegistryLookup(top /* allowLookup*/)
                         .setOriginatingPendingIntent(originatingPendingIntent)
-                        .setBackgroundStartPrivileges(forcedBalByPiSender);
+                        .setAllowBalExemptionForSystemProcess(allowBalExemptionForSystemProcess);
             }
             // Log if the activities to be started have different uids.
             if (startingUidPkgs.size() > 1) {
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 8dab717..e15968d 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -95,7 +95,6 @@
 import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.app.ActivityOptions;
-import android.app.BackgroundStartPrivileges;
 import android.app.IApplicationThread;
 import android.app.PendingIntent;
 import android.app.ProfilerInfo;
@@ -430,7 +429,7 @@
         WaitResult waitResult;
         int filterCallingUid;
         PendingIntentRecord originatingPendingIntent;
-        BackgroundStartPrivileges forcedBalByPiSender;
+        boolean allowBalExemptionForSystemProcess;
         boolean freezeScreen;
 
         final StringBuilder logMessage = new StringBuilder();
@@ -496,7 +495,7 @@
             allowPendingRemoteAnimationRegistryLookup = true;
             filterCallingUid = UserHandle.USER_NULL;
             originatingPendingIntent = null;
-            forcedBalByPiSender = BackgroundStartPrivileges.NONE;
+            allowBalExemptionForSystemProcess = false;
             freezeScreen = false;
             errorCallbackToken = null;
         }
@@ -540,7 +539,7 @@
                     = request.allowPendingRemoteAnimationRegistryLookup;
             filterCallingUid = request.filterCallingUid;
             originatingPendingIntent = request.originatingPendingIntent;
-            forcedBalByPiSender = request.forcedBalByPiSender;
+            allowBalExemptionForSystemProcess = request.allowBalExemptionForSystemProcess;
             freezeScreen = request.freezeScreen;
             errorCallbackToken = request.errorCallbackToken;
         }
@@ -1275,8 +1274,8 @@
                         "Creator PermissionPolicyService.checkStartActivity Caused abortion.",
                         intent, intentCreatorUid, intentCreatorPackage, callingUid, callingPackage);
             }
-            intent.removeCreatorTokenInfo();
         }
+        intent.removeCreatorToken();
 
         // Merge the two options bundles, while realCallerOptions takes precedence.
         ActivityOptions checkedOptions = options != null
@@ -1298,7 +1297,7 @@
                             realCallingPid,
                             callerApp,
                             request.originatingPendingIntent,
-                            request.forcedBalByPiSender,
+                            request.allowBalExemptionForSystemProcess,
                             resultRecord,
                             intent,
                             checkedOptions);
@@ -2398,7 +2397,8 @@
         // This is moving an existing task to front. But since dream activity has a higher z-order
         // to cover normal activities, it needs the awakening event to be dismissed.
         if (mService.isDreaming() && targetTaskTop.canTurnScreenOn()) {
-            targetTaskTop.mTaskSupervisor.wakeUp("recycleTask#turnScreenOnFlag");
+            targetTaskTop.mTaskSupervisor.wakeUp(
+                    targetTaskTop.getDisplayId(), "recycleTask#turnScreenOnFlag");
         }
 
         mLastStartActivityRecord = targetTaskTop;
@@ -3469,8 +3469,8 @@
         return this;
     }
 
-    ActivityStarter setActivityOptions(Bundle bOptions) {
-        return setActivityOptions(SafeActivityOptions.fromBundle(bOptions));
+    ActivityStarter setActivityOptions(Bundle bOptions, int callingPid, int callingUid) {
+        return setActivityOptions(SafeActivityOptions.fromBundle(bOptions, callingPid, callingUid));
     }
 
     ActivityStarter setIgnoreTargetSecurity(boolean ignoreTargetSecurity) {
@@ -3533,8 +3533,9 @@
         return this;
     }
 
-    ActivityStarter setBackgroundStartPrivileges(BackgroundStartPrivileges forcedBalByPiSender) {
-        mRequest.forcedBalByPiSender = forcedBalByPiSender;
+    ActivityStarter setAllowBalExemptionForSystemProcess(
+            boolean allowBalExemptionForSystemProcess) {
+        mRequest.allowBalExemptionForSystemProcess = allowBalExemptionForSystemProcess;
         return this;
     }
 
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
index 3560565..0a57cb5 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
@@ -21,7 +21,6 @@
 import android.annotation.UserIdInt;
 import android.app.ActivityManager;
 import android.app.AppProtoEnums;
-import android.app.BackgroundStartPrivileges;
 import android.app.IActivityManager;
 import android.app.IAppTask;
 import android.app.IApplicationThread;
@@ -179,15 +178,15 @@
      * @param validateIncomingUser Set true to skip checking {@code userId} with the calling UID.
      * @param originatingPendingIntent PendingIntentRecord that originated this activity start or
      *        null if not originated by PendingIntent
-     * @param forcedBalByPiSender If set to allow, the
-     *        PendingIntent's sender will try to force allow background activity starts.
+     * @param allowBalExemptionForSystemProcess If set to {@code true}, the
+     *        PendingIntent's sender will allow additional exemptions.
      *        This is only possible if the sender of the PendingIntent is a system process.
      */
     public abstract int startActivitiesInPackage(int uid, int realCallingPid, int realCallingUid,
             String callingPackage, @Nullable String callingFeatureId, Intent[] intents,
             String[] resolvedTypes, IBinder resultTo, SafeActivityOptions options, int userId,
             boolean validateIncomingUser, PendingIntentRecord originatingPendingIntent,
-            BackgroundStartPrivileges forcedBalByPiSender);
+            boolean allowBalExemptionForSystemProcess);
 
     /**
      * Start intent as a package.
@@ -202,8 +201,8 @@
      * @param validateIncomingUser Set true to skip checking {@code userId} with the calling UID.
      * @param originatingPendingIntent PendingIntentRecord that originated this activity start or
      *        null if not originated by PendingIntent
-     * @param forcedBalByPiSender If set to allow, the
-     *        PendingIntent's sender will try to force allow background activity starts.
+     * @param allowBalExemptionForSystemProcess If set to {@code true}, the
+     *        PendingIntent's sender will allow additional exemptions.
      *        This is only possible if the sender of the PendingIntent is a system process.
      */
     public abstract int startActivityInPackage(int uid, int realCallingPid, int realCallingUid,
@@ -211,7 +210,7 @@
             String resolvedType, IBinder resultTo, String resultWho, int requestCode,
             int startFlags, SafeActivityOptions options, int userId, Task inTask, String reason,
             boolean validateIncomingUser, PendingIntentRecord originatingPendingIntent,
-            BackgroundStartPrivileges forcedBalByPiSender);
+            boolean allowBalExemptionForSystemProcess);
 
     /**
      * Callback to be called on certain activity start scenarios.
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 111e74e..0745fec 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -145,7 +145,6 @@
 import android.app.AnrController;
 import android.app.AppGlobals;
 import android.app.AppOpsManager;
-import android.app.BackgroundStartPrivileges;
 import android.app.Dialog;
 import android.app.IActivityClientController;
 import android.app.IActivityController;
@@ -1228,7 +1227,6 @@
             String callingFeatureId, Intent intent, String resolvedType, IBinder resultTo,
             String resultWho, int requestCode, int startFlags, ProfilerInfo profilerInfo,
             Bundle bOptions) {
-        mAmInternal.addCreatorToken(intent, callingPackage);
         return startActivityAsUser(caller, callingPackage, callingFeatureId, intent, resolvedType,
                 resultTo, resultWho, requestCode, startFlags, profilerInfo, bOptions,
                 UserHandle.getCallingUserId());
@@ -1246,12 +1244,14 @@
                 mAmInternal.addCreatorToken(intent, callingPackage);
             }
         }
-        userId = handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), userId, reason);
+        final int callingPid = Binder.getCallingPid();
+        final int callingUid = Binder.getCallingUid();
+        userId = handleIncomingUser(callingPid, callingUid, userId, reason);
         // TODO: Switch to user app stacks here.
         return getActivityStartController().startActivities(caller, -1, 0, -1, callingPackage,
                 callingFeatureId, intents, resolvedTypes, resultTo,
-                SafeActivityOptions.fromBundle(bOptions), userId, reason,
-                null /* originatingPendingIntent */, BackgroundStartPrivileges.NONE);
+                SafeActivityOptions.fromBundle(bOptions, callingPid, callingUid), userId, reason,
+                null /* originatingPendingIntent */, false);
     }
 
     @Override
@@ -1276,7 +1276,10 @@
             IBinder resultTo, String resultWho, int requestCode, int startFlags,
             ProfilerInfo profilerInfo, Bundle bOptions, int userId, boolean validateIncomingUser) {
         mAmInternal.addCreatorToken(intent, callingPackage);
-        final SafeActivityOptions opts = SafeActivityOptions.fromBundle(bOptions);
+        final int callingPid = Binder.getCallingPid();
+        final int callingUid = Binder.getCallingUid();
+        final SafeActivityOptions opts = SafeActivityOptions.fromBundle(
+                bOptions, callingPid, callingUid);
 
         assertPackageMatchesCallingUid(callingPackage);
         enforceNotIsolatedCaller("startActivityAsUser");
@@ -1285,11 +1288,11 @@
             SdkSandboxManagerLocal sdkSandboxManagerLocal = LocalManagerRegistry.getManager(
                     SdkSandboxManagerLocal.class);
             sdkSandboxManagerLocal.enforceAllowedToHostSandboxedActivity(
-                    intent, Binder.getCallingUid(), callingPackage
+                    intent, callingUid, callingPackage
             );
         }
 
-        if (Process.isSdkSandboxUid(Binder.getCallingUid())) {
+        if (Process.isSdkSandboxUid(callingUid)) {
             SdkSandboxManagerLocal sdkSandboxManagerLocal = LocalManagerRegistry.getManager(
                     SdkSandboxManagerLocal.class);
             if (sdkSandboxManagerLocal == null) {
@@ -1300,7 +1303,7 @@
         }
 
         userId = getActivityStartController().checkTargetUser(userId, validateIncomingUser,
-                Binder.getCallingPid(), Binder.getCallingUid(), "startActivityAsUser");
+                callingPid, callingUid, "startActivityAsUser");
 
         // TODO: Switch to user app stacks here.
         return getActivityStartController().obtainStarter(intent, "startActivityAsUser")
@@ -1365,7 +1368,10 @@
             throw new IllegalArgumentException("File descriptors passed in Intent");
         }
 
-        SafeActivityOptions options = SafeActivityOptions.fromBundle(bOptions);
+        final int origCallingPid = Binder.getCallingPid();
+        final int origCallingUid = Binder.getCallingUid();
+        SafeActivityOptions options = SafeActivityOptions.fromBundle(bOptions, origCallingPid,
+                origCallingUid);
 
         synchronized (mGlobalLock) {
             final ActivityRecord r = ActivityRecord.isInRootTaskLocked(callingActivity);
@@ -1454,13 +1460,12 @@
                 resultTo.removeResultsLocked(r, resultWho, requestCode);
             }
 
-            final int origCallingUid = Binder.getCallingUid();
-            final int origCallingPid = Binder.getCallingPid();
             final long origId = Binder.clearCallingIdentity();
             // TODO(b/64750076): Check if calling pid should really be -1.
             try {
                 if (options == null) {
-                    options = new SafeActivityOptions(ActivityOptions.makeBasic());
+                    options = new SafeActivityOptions(ActivityOptions.makeBasic(),
+                            Binder.getCallingPid(), Binder.getCallingUid());
                 }
 
                 // Fixes b/230492947 b/337726734
@@ -1560,7 +1565,7 @@
                     // To start the dream from background, we need to start it from a persistent
                     // system process. Here we set the real calling uid to the system server uid
                     .setRealCallingUid(Binder.getCallingUid())
-                    .setBackgroundStartPrivileges(BackgroundStartPrivileges.ALLOW_BAL)
+                    .setAllowBalExemptionForSystemProcess(true)
                     .execute();
 
             final ActivityRecord started = outActivity[0];
@@ -1578,8 +1583,9 @@
         assertPackageMatchesCallingUid(callingPackage);
         final WaitResult res = new WaitResult();
         enforceNotIsolatedCaller("startActivityAndWait");
-        userId = handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
-                userId, "startActivityAndWait");
+        final int callingPid = Binder.getCallingPid();
+        final int callingUid = Binder.getCallingUid();
+        userId = handleIncomingUser(callingPid, callingUid, userId, "startActivityAndWait");
         // TODO: Switch to user app stacks here.
         getActivityStartController().obtainStarter(intent, "startActivityAndWait")
                 .setCaller(caller)
@@ -1590,7 +1596,7 @@
                 .setResultWho(resultWho)
                 .setRequestCode(requestCode)
                 .setStartFlags(startFlags)
-                .setActivityOptions(bOptions)
+                .setActivityOptions(bOptions, callingPid, callingUid)
                 .setUserId(userId)
                 .setProfilerInfo(profilerInfo)
                 .setWaitResult(res)
@@ -1605,8 +1611,9 @@
             Bundle bOptions, int userId) {
         assertPackageMatchesCallingUid(callingPackage);
         enforceNotIsolatedCaller("startActivityWithConfig");
-        userId = handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), userId,
-                "startActivityWithConfig");
+        final int callingPid = Binder.getCallingPid();
+        final int callingUid = Binder.getCallingUid();
+        userId = handleIncomingUser(callingPid, callingUid, userId, "startActivityWithConfig");
         // TODO: Switch to user app stacks here.
         return getActivityStartController().obtainStarter(intent, "startActivityWithConfig")
                 .setCaller(caller)
@@ -1618,7 +1625,7 @@
                 .setRequestCode(requestCode)
                 .setStartFlags(startFlags)
                 .setGlobalConfiguration(config)
-                .setActivityOptions(bOptions)
+                .setActivityOptions(bOptions, callingPid, callingUid)
                 .setUserId(userId)
                 .execute();
     }
@@ -1711,7 +1718,7 @@
                     .setFilterCallingUid(isResolver ? 0 /* system */ : targetUid)
                     // The target may well be in the background, which would normally prevent it
                     // from starting an activity. Here we definitely want the start to succeed.
-                    .setBackgroundStartPrivileges(BackgroundStartPrivileges.ALLOW_BAL)
+                    .setAllowBalExemptionForSystemProcess(true)
                     .execute();
         } catch (SecurityException e) {
             // XXX need to figure out how to propagate to original app.
@@ -1757,7 +1764,7 @@
                 .setProfilerInfo(profilerInfo)
                 .setActivityOptions(createSafeActivityOptionsWithBalAllowed(bOptions))
                 .setUserId(userId)
-                .setBackgroundStartPrivileges(BackgroundStartPrivileges.ALLOW_BAL)
+                .setAllowBalExemptionForSystemProcess(true)
                 .execute();
     }
 
@@ -1784,7 +1791,7 @@
                     .setResolvedType(resolvedType)
                     .setActivityOptions(createSafeActivityOptionsWithBalAllowed(bOptions))
                     .setUserId(userId)
-                    .setBackgroundStartPrivileges(BackgroundStartPrivileges.ALLOW_BAL)
+                    .setAllowBalExemptionForSystemProcess(true)
                     .execute();
         } finally {
             Binder.restoreCallingIdentity(origId);
@@ -1822,7 +1829,8 @@
 
         final int callingPid = Binder.getCallingPid();
         final int callingUid = Binder.getCallingUid();
-        final SafeActivityOptions safeOptions = SafeActivityOptions.fromBundle(bOptions);
+        final SafeActivityOptions safeOptions = SafeActivityOptions.fromBundle(
+                bOptions, callingPid, callingUid);
         final long origId = Binder.clearCallingIdentity();
         try {
             return mTaskSupervisor.startActivityFromRecents(callingPid, callingUid, taskId,
@@ -1848,6 +1856,9 @@
 
         final ActivityOptions activityOptions = ActivityOptions.makeBasic();
         activityOptions.setLaunchTaskId(taskId);
+        // Pass in the system UID to allow setting launch taskId with MANAGE_GAME_ACTIVITY.
+        final SafeActivityOptions safeOptions = new SafeActivityOptions(
+                activityOptions, Process.myPid(), Process.SYSTEM_UID);
 
         userId = handleIncomingUser(callingPid, callingUid, userId, "startActivityFromGameSession");
 
@@ -1861,7 +1872,7 @@
                     .setCallingPackage(intent.getPackage())
                     .setCallingFeatureId(callingFeatureId)
                     .setUserId(userId)
-                    .setActivityOptions(activityOptions.toBundle())
+                    .setActivityOptions(safeOptions)
                     .setRealCallingUid(Binder.getCallingUid())
                     .execute();
         } finally {
@@ -2230,8 +2241,10 @@
 
         ProtoLog.d(WM_DEBUG_TASKS, "moveTaskToFront: moving taskId=%d", taskId);
         synchronized (mGlobalLock) {
+            final int callingPid = Binder.getCallingPid();
+            final int callingUid = Binder.getCallingUid();
             moveTaskToFrontLocked(appThread, callingPackage, taskId, flags,
-                    SafeActivityOptions.fromBundle(bOptions));
+                    SafeActivityOptions.fromBundle(bOptions, callingPid, callingUid));
         }
     }
 
@@ -2256,7 +2269,7 @@
                 -1,
                 callerApp,
                 null,
-                BackgroundStartPrivileges.NONE,
+                false,
                 null,
                 null,
                 null);
@@ -5883,6 +5896,29 @@
     }
 
     /**
+     * Registers an app that uses the Strict Mode for detecting BAL.
+     *
+     * @param callback the callback to register
+     * @return {@code true} if the callback was registered successfully.
+     */
+    @Override
+    public boolean registerBackgroundActivityStartCallback(IBinder callback) {
+        return mTaskSupervisor.getBackgroundActivityLaunchController()
+                .addStrictModeCallback(Binder.getCallingUid(), callback);
+    }
+
+    /**
+     * Unregisters an app that uses the Strict Mode for detecting BAL.
+     *
+     * @param callback the callback to unregister
+     */
+    @Override
+    public void unregisterBackgroundActivityStartCallback(IBinder callback) {
+        mTaskSupervisor.getBackgroundActivityLaunchController()
+                .removeStrictModeCallback(Binder.getCallingUid(), callback);
+    }
+
+    /**
      * Wrap the {@link ActivityOptions} in {@link SafeActivityOptions} and attach caller options
      * that allow using the callers permissions to start background activities.
      */
@@ -5896,7 +5932,7 @@
             options.setPendingIntentBackgroundActivityStartMode(
                     ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED);
         }
-        return new SafeActivityOptions(options);
+        return new SafeActivityOptions(options, Binder.getCallingPid(), Binder.getCallingUid());
     }
 
     /**
@@ -6061,12 +6097,14 @@
                 Binder.restoreCallingIdentity(ident);
             }
 
+            final int callingPid = Binder.getCallingPid();
+            final int callingUid = Binder.getCallingUid();
             return getActivityStartController().startActivitiesInPackage(
                     packageUid, packageName, featureId,
                     intents, resolvedTypes, null /* resultTo */,
-                    SafeActivityOptions.fromBundle(bOptions), userId,
+                    SafeActivityOptions.fromBundle(bOptions, callingPid, callingUid), userId,
                     false /* validateIncomingUser */, null /* originatingPendingIntent */,
-                    BackgroundStartPrivileges.NONE);
+                    false);
         }
 
         @Override
@@ -6074,12 +6112,12 @@
                 String callingPackage, @Nullable String callingFeatureId, Intent[] intents,
                 String[] resolvedTypes, IBinder resultTo, SafeActivityOptions options, int userId,
                 boolean validateIncomingUser, PendingIntentRecord originatingPendingIntent,
-                BackgroundStartPrivileges forcedBalByPiSender) {
+                boolean allowBalExemptionForSystemProcess) {
             assertPackageMatchesCallingUid(callingPackage);
             return getActivityStartController().startActivitiesInPackage(uid, realCallingPid,
                     realCallingUid, callingPackage, callingFeatureId, intents, resolvedTypes,
                     resultTo, options, userId, validateIncomingUser, originatingPendingIntent,
-                    forcedBalByPiSender);
+                    allowBalExemptionForSystemProcess);
         }
 
         @Override
@@ -6088,13 +6126,13 @@
                 String resolvedType, IBinder resultTo, String resultWho, int requestCode,
                 int startFlags, SafeActivityOptions options, int userId, Task inTask, String reason,
                 boolean validateIncomingUser, PendingIntentRecord originatingPendingIntent,
-                BackgroundStartPrivileges forcedBalByPiSender) {
+                boolean allowBalExemptionForSystemProcess) {
             assertPackageMatchesCallingUid(callingPackage);
             return getActivityStartController().startActivityInPackage(uid, realCallingPid,
                     realCallingUid, callingPackage, callingFeatureId, intent, resolvedType,
                     resultTo, resultWho, requestCode, startFlags, options, userId, inTask,
                     reason, validateIncomingUser, originatingPendingIntent,
-                    forcedBalByPiSender);
+                    allowBalExemptionForSystemProcess);
         }
 
         @Override
@@ -6125,7 +6163,7 @@
                     .setActivityOptions(createSafeActivityOptionsWithBalAllowed(options))
                     .setRealCallingUid(Binder.getCallingUid())
                     .setUserId(userId)
-                    .setBackgroundStartPrivileges(BackgroundStartPrivileges.ALLOW_BAL)
+                    .setAllowBalExemptionForSystemProcess(true)
                     .setFreezeScreen(true)
                     .execute();
         }
diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
index 6a1f28d..30b53d1 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
@@ -96,7 +96,6 @@
 import android.app.ActivityOptions;
 import android.app.AppOpsManager;
 import android.app.AppOpsManagerInternal;
-import android.app.BackgroundStartPrivileges;
 import android.app.IActivityClientController;
 import android.app.ProfilerInfo;
 import android.app.ResultInfo;
@@ -242,6 +241,8 @@
     static {
         ACTION_TO_RUNTIME_PERMISSION.put(MediaStore.ACTION_IMAGE_CAPTURE,
                 Manifest.permission.CAMERA);
+        ACTION_TO_RUNTIME_PERMISSION.put(MediaStore.ACTION_MOTION_PHOTO_CAPTURE,
+                Manifest.permission.CAMERA);
         ACTION_TO_RUNTIME_PERMISSION.put(MediaStore.ACTION_VIDEO_CAPTURE,
                 Manifest.permission.CAMERA);
         ACTION_TO_RUNTIME_PERMISSION.put(Intent.ACTION_CALL,
@@ -2524,9 +2525,9 @@
         }
     }
 
-    void wakeUp(String reason) {
+    void wakeUp(int displayId, String reason) {
         mPowerManager.wakeUp(SystemClock.uptimeMillis(), PowerManager.WAKE_REASON_APPLICATION,
-                "android.server.am:TURN_ON:" + reason);
+                "android.server.am:TURN_ON:" + reason, displayId);
     }
 
     /** Starts a batch of visibility updates. */
@@ -2873,7 +2874,7 @@
                     callingPid, callingUid, callingPackage, callingFeatureId, intent, null, null,
                     null, 0, 0, options, userId, task, "startActivityFromRecents",
                     false /* validateIncomingUser */, null /* originatingPendingIntent */,
-                    BackgroundStartPrivileges.NONE);
+                    /* allowBalExemptionForSystemProcess */ false);
         } finally {
             SaferIntentUtils.DISABLE_ENFORCE_INTENTS_TO_MATCH_INTENT_FILTERS.set(false);
             synchronized (mService.mGlobalLock) {
diff --git a/services/core/java/com/android/server/wm/AppTaskImpl.java b/services/core/java/com/android/server/wm/AppTaskImpl.java
index eee4c86..c845c50 100644
--- a/services/core/java/com/android/server/wm/AppTaskImpl.java
+++ b/services/core/java/com/android/server/wm/AppTaskImpl.java
@@ -17,12 +17,11 @@
 package com.android.server.wm;
 
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ACTIVITY_STARTS;
-import static com.android.server.wm.BackgroundActivityStartController.BalVerdict;
 import static com.android.server.wm.ActivityTaskSupervisor.REMOVE_FROM_RECENTS;
+import static com.android.server.wm.BackgroundActivityStartController.BalVerdict;
 import static com.android.server.wm.RootWindowContainer.MATCH_ATTACHED_TASK_OR_RECENT_TASKS;
 
 import android.app.ActivityManager;
-import android.app.BackgroundStartPrivileges;
 import android.app.IAppTask;
 import android.app.IApplicationThread;
 import android.content.Intent;
@@ -136,7 +135,7 @@
                         -1,
                         callerApp,
                         null,
-                        BackgroundStartPrivileges.NONE,
+                        false,
                         null,
                         null,
                         null);
@@ -176,13 +175,14 @@
                 throw new IllegalArgumentException("Bad app thread " + appThread);
             }
         }
-
+        final int callingPid = Binder.getCallingPid();
+        final int callingUid = Binder.getCallingUid();
         return mService.getActivityStartController().obtainStarter(intent, "AppTaskImpl")
                 .setCaller(appThread)
                 .setCallingPackage(callingPackage)
                 .setCallingFeatureId(callingFeatureId)
                 .setResolvedType(resolvedType)
-                .setActivityOptions(bOptions)
+                .setActivityOptions(bOptions, callingPid, callingUid)
                 .setUserId(callingUser)
                 .setInTask(task)
                 .execute();
diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java
index 6cc4b1e..5c53c2a 100644
--- a/services/core/java/com/android/server/wm/BackNavigationController.java
+++ b/services/core/java/com/android/server/wm/BackNavigationController.java
@@ -1726,8 +1726,7 @@
                 ActivityRecord currentActivity,
                 ArrayList<ActivityRecord> previousActivity,
                 WindowContainer removedWindowContainer) {
-            final ScheduleAnimationBuilder builder =
-                    new ScheduleAnimationBuilder(backType, adapter, monitor);
+            final ScheduleAnimationBuilder builder = new ScheduleAnimationBuilder(adapter, monitor);
             switch (backType) {
                 case BackNavigationInfo.TYPE_RETURN_TO_HOME:
                     return builder
@@ -1752,7 +1751,6 @@
         }
 
         class ScheduleAnimationBuilder {
-            final int mType;
             final BackAnimationAdapter mBackAnimationAdapter;
             final NavigationMonitor mNavigationMonitor;
             WindowContainer mCloseTarget;
@@ -1760,9 +1758,8 @@
             boolean mIsLaunchBehind;
             TaskSnapshot mSnapshot;
 
-            ScheduleAnimationBuilder(int type, BackAnimationAdapter adapter,
+            ScheduleAnimationBuilder(BackAnimationAdapter adapter,
                     NavigationMonitor monitor) {
-                mType = type;
                 mBackAnimationAdapter = adapter;
                 mNavigationMonitor = monitor;
             }
@@ -1793,7 +1790,36 @@
             }
 
             private Transition prepareTransitionIfNeeded(ActivityRecord[] visibleOpenActivities) {
-                if (mSnapshot == null) {
+                if (Flags.unifyBackNavigationTransition()) {
+                    if (mCloseTarget.asWindowState() != null) {
+                        return null;
+                    }
+                    final ArrayList<ActivityRecord> makeVisibles = new ArrayList<>();
+                    for (int i = visibleOpenActivities.length - 1; i >= 0; --i) {
+                        final ActivityRecord activity = visibleOpenActivities[i];
+                        if (activity.mLaunchTaskBehind || activity.isVisibleRequested()) {
+                            continue;
+                        }
+                        makeVisibles.add(activity);
+                    }
+                    final TransitionController tc = visibleOpenActivities[0].mTransitionController;
+                    final Transition prepareOpen = tc.createTransition(
+                            TRANSIT_PREPARE_BACK_NAVIGATION);
+                    tc.collect(mCloseTarget);
+                    prepareOpen.setBackGestureAnimation(mCloseTarget, true /* isTop */);
+                    for (int i = mOpenTargets.length - 1; i >= 0; --i) {
+                        tc.collect(mOpenTargets[i]);
+                        prepareOpen.setBackGestureAnimation(mOpenTargets[i], false /* isTop */);
+                    }
+                    if (!makeVisibles.isEmpty()) {
+                        setLaunchBehind(visibleOpenActivities);
+                    }
+                    tc.requestStartTransition(prepareOpen,
+                            null /*startTask */, null /* remoteTransition */,
+                            null /* displayChange */);
+                    prepareOpen.setReady(makeVisibles.get(0), true);
+                    return prepareOpen;
+                } else if (mSnapshot == null) {
                     return setLaunchBehind(visibleOpenActivities);
                 }
                 return null;
@@ -1990,6 +2016,7 @@
 
     private static Transition setLaunchBehind(@NonNull ActivityRecord[] activities) {
         final boolean migrateBackTransition = Flags.migratePredictiveBackTransition();
+        final boolean unifyBackNavigationTransition = Flags.unifyBackNavigationTransition();
         final ArrayList<ActivityRecord> affects = new ArrayList<>();
         for (int i = activities.length - 1; i >= 0; --i) {
             final ActivityRecord activity = activities[i];
@@ -2003,8 +2030,8 @@
         }
 
         final TransitionController tc = activities[0].mTransitionController;
-        final Transition prepareOpen = migrateBackTransition && !tc.isCollecting()
-                ? tc.createTransition(TRANSIT_PREPARE_BACK_NAVIGATION) : null;
+        final Transition prepareOpen = migrateBackTransition && !unifyBackNavigationTransition
+                && !tc.isCollecting() ? tc.createTransition(TRANSIT_PREPARE_BACK_NAVIGATION) : null;
 
         DisplayContent commonDisplay = null;
         for (int i = affects.size() - 1; i >= 0; --i) {
diff --git a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
index f9902cf..a462c67 100644
--- a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
+++ b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
@@ -52,6 +52,7 @@
 import static com.android.window.flags.Flags.balRequireOptInSameUid;
 import static com.android.window.flags.Flags.balRespectAppSwitchStateWhenCheckBoundByForegroundUid;
 import static com.android.window.flags.Flags.balShowToastsBlocked;
+import static com.android.window.flags.Flags.balStrictMode;
 
 import static java.lang.annotation.RetentionPolicy.SOURCE;
 import static java.util.Objects.requireNonNull;
@@ -63,21 +64,25 @@
 import android.app.ActivityOptions;
 import android.app.AppOpsManager;
 import android.app.BackgroundStartPrivileges;
+import android.app.IBackgroundActivityLaunchCallback;
 import android.app.compat.CompatChanges;
 import android.compat.annotation.ChangeId;
 import android.compat.annotation.EnabledAfter;
-import android.compat.annotation.Overridable;
 import android.content.ComponentName;
 import android.content.Intent;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
+import android.os.IBinder;
 import android.os.Process;
+import android.os.RemoteException;
 import android.os.SystemClock;
 import android.os.UserHandle;
 import android.provider.DeviceConfig;
+import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.DebugUtils;
 import android.util.Slog;
+import android.util.SparseArray;
 import android.widget.Toast;
 
 import com.android.internal.R;
@@ -92,6 +97,7 @@
 import java.lang.annotation.Retention;
 import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.Map;
 import java.util.StringJoiner;
 import java.util.function.Consumer;
 import java.util.function.Function;
@@ -130,7 +136,6 @@
     /** If enabled the creator will not allow BAL on its behalf by default. */
     @ChangeId
     @EnabledAfter(targetSdkVersion = UPSIDE_DOWN_CAKE)
-    @Overridable
     private static final long DEFAULT_RESCIND_BAL_PRIVILEGES_FROM_PENDING_INTENT_CREATOR =
             296478951;
     public static final ActivityOptions ACTIVITY_OPTIONS_SYSTEM_DEFINED =
@@ -143,6 +148,10 @@
     private final ActivityTaskManagerService mService;
 
     private final ActivityTaskSupervisor mSupervisor;
+    @GuardedBy("mStrictModeBalCallbacks")
+    private final SparseArray<ArrayMap<IBinder, IBackgroundActivityLaunchCallback>>
+            mStrictModeBalCallbacks = new SparseArray<>();
+
 
     // TODO(b/263368846) Rename when ASM logic is moved in
     @Retention(SOURCE)
@@ -304,7 +313,7 @@
         private final @ActivityManager.ProcessState int mRealCallingUidProcState;
         private final boolean mIsRealCallingUidPersistentSystemProcess;
         private final PendingIntentRecord mOriginatingPendingIntent;
-        private final BackgroundStartPrivileges mForcedBalByPiSender;
+        private final boolean mAllowBalExemptionForSystemProcess;
         private final Intent mIntent;
         private final WindowProcessController mCallerApp;
         private final WindowProcessController mRealCallerApp;
@@ -319,7 +328,7 @@
                  int realCallingUid, int realCallingPid,
                  WindowProcessController callerApp,
                  PendingIntentRecord originatingPendingIntent,
-                 BackgroundStartPrivileges forcedBalByPiSender,
+                 boolean allowBalExemptionForSystemProcess,
                  ActivityRecord resultRecord,
                  Intent intent,
                  ActivityOptions checkedOptions) {
@@ -329,7 +338,7 @@
             mRealCallingUid = realCallingUid;
             mRealCallingPid = realCallingPid;
             mCallerApp = callerApp;
-            mForcedBalByPiSender = forcedBalByPiSender;
+            mAllowBalExemptionForSystemProcess = allowBalExemptionForSystemProcess;
             mOriginatingPendingIntent = originatingPendingIntent;
             mIntent = intent;
             mRealCallingPackage = mService.getPackageNameIfUnique(realCallingUid, realCallingPid);
@@ -549,7 +558,8 @@
                     ActivityManager.class, "PROCESS_STATE_", mCallingUidProcState));
             sb.append("; isCallingUidPersistentSystemProcess: ")
                     .append(mIsCallingUidPersistentSystemProcess);
-            sb.append("; forcedBalByPiSender: ").append(mForcedBalByPiSender);
+            sb.append("; allowBalExemptionForSystemProcess: ")
+                    .append(mAllowBalExemptionForSystemProcess);
             sb.append("; intent: ").append(mIntent);
             sb.append("; callerApp: ").append(mCallerApp);
             if (mCallerApp != null) {
@@ -704,8 +714,8 @@
      * @param callerApp The process that calls this method (only if not a PendingIntent)
      * @param originatingPendingIntent PendingIntentRecord that originated this activity start or
      *        null if not originated by PendingIntent
-     * @param forcedBalByPiSender If set to allow, the
-     *        PendingIntent's sender will try to force allow background activity starts.
+     * @param allowBalExemptionForSystemProcess If set to true, the
+     *        PendingIntent's sender will allow additional exemptions.
      *        This is only possible if the sender of the PendingIntent is a system process.
      * @param resultRecord If not null, this indicates that the caller expects a result.
      * @param intent Intent that should be started.
@@ -722,7 +732,7 @@
             int realCallingPid,
             WindowProcessController callerApp,
             PendingIntentRecord originatingPendingIntent,
-            BackgroundStartPrivileges forcedBalByPiSender,
+            boolean allowBalExemptionForSystemProcess,
             ActivityRecord resultRecord,
             Intent intent,
             ActivityOptions checkedOptions) {
@@ -734,7 +744,7 @@
 
         BalState state = new BalState(callingUid, callingPid, callingPackage,
                 realCallingUid, realCallingPid, callerApp, originatingPendingIntent,
-                forcedBalByPiSender, resultRecord, intent, checkedOptions);
+                allowBalExemptionForSystemProcess, resultRecord, intent, checkedOptions);
 
         // In the case of an SDK sandbox calling uid, check if the corresponding app uid has a
         // visible window.
@@ -840,7 +850,120 @@
             // only show a toast if either caller or real caller could launch if they opted in
             showToast("BAL blocked. goo.gle/android-bal");
         }
-        return statsLog(BalVerdict.BLOCK, state);
+        BalVerdict verdict = statsLog(BalVerdict.BLOCK, state);
+        if (balStrictMode()) {
+            String abortDebugMessage;
+            if (state.isPendingIntent()) {
+                abortDebugMessage =
+                        "PendingIntent Activity start blocked in " + state.mRealCallingPackage
+                                + ". "
+                                + "PendingIntent was created in " + state.mCallingPackage
+                                + ". "
+                                + (state.mResultForRealCaller.allows()
+                                ? state.mRealCallingPackage
+                                + " could opt in to grant BAL privileges when sending. "
+                                : "")
+                                + (state.mResultForCaller.allows()
+                                ? state.mCallingPackage
+                                + " could opt in to grant BAL privileges when creating."
+                                : "")
+                                + "The intent would have started " + state.mIntent.getComponent();
+            } else {
+                abortDebugMessage = "Activity start blocked. "
+                        + "The intent would have started " + state.mIntent.getComponent();
+            }
+            strictModeLaunchAborted(state.mCallingUid, abortDebugMessage);
+            if (!state.callerIsRealCaller()) {
+                strictModeLaunchAborted(state.mRealCallingUid, abortDebugMessage);
+            }
+        }
+        return verdict;
+    }
+
+    /**
+     * Retrieve a registered strict mode callback for BAL.
+     * @param uid the uid of the app.
+     * @return the callback if it exists, returns <code>null</code> otherwise.
+     */
+    @Nullable
+    Map<IBinder, IBackgroundActivityLaunchCallback> getStrictModeBalCallbacks(int uid) {
+        ArrayMap<IBinder, IBackgroundActivityLaunchCallback> callbackMap;
+        synchronized (mStrictModeBalCallbacks) {
+            callbackMap =
+                    mStrictModeBalCallbacks.get(uid);
+            if (callbackMap == null) {
+                return null;
+            }
+            return new ArrayMap<>(callbackMap);
+        }
+    }
+
+    /**
+     * Add strict mode callback for BAL.
+     *
+     * @param uid      the UID for which the binder is registered.
+     * @param callback the {@link IBackgroundActivityLaunchCallback} binder to call when BAL is
+     *                 blocked.
+     * @return {@code true} if the callback has been successfully added.
+     */
+    boolean addStrictModeCallback(int uid, IBinder callback) {
+        IBackgroundActivityLaunchCallback balCallback =
+                IBackgroundActivityLaunchCallback.Stub.asInterface(callback);
+        synchronized (mStrictModeBalCallbacks) {
+            ArrayMap<IBinder, IBackgroundActivityLaunchCallback> callbackMap =
+                    mStrictModeBalCallbacks.get(uid);
+            if (callbackMap == null) {
+                callbackMap = new ArrayMap<>();
+                mStrictModeBalCallbacks.put(uid, callbackMap);
+            }
+            if (callbackMap.containsKey(callback)) {
+                return false;
+            }
+            callbackMap.put(callback, balCallback);
+        }
+        try {
+            callback.linkToDeath(() -> removeStrictModeCallback(uid, callback), 0);
+        } catch (RemoteException e) {
+            removeStrictModeCallback(uid, callback);
+        }
+        return true;
+    }
+
+    /**
+     * Remove strict mode callback for BAL.
+     *
+     * @param uid      the UID for which the binder is registered.
+     * @param callback the {@link IBackgroundActivityLaunchCallback} binder to call when BAL is
+     *                 blocked.
+     */
+    void removeStrictModeCallback(int uid, IBinder callback) {
+        synchronized (mStrictModeBalCallbacks) {
+            Map<IBinder, IBackgroundActivityLaunchCallback> callbackMap =
+                    mStrictModeBalCallbacks.get(uid);
+            if (callback == null || !callbackMap.containsKey(callback)) {
+                return;
+            }
+            callbackMap.remove(callback);
+            if (callbackMap.isEmpty()) {
+                mStrictModeBalCallbacks.remove(uid);
+            }
+        }
+    }
+
+    private void strictModeLaunchAborted(int callingUid, String message) {
+        Map<IBinder, IBackgroundActivityLaunchCallback> strictModeBalCallbacks =
+                getStrictModeBalCallbacks(callingUid);
+        if (strictModeBalCallbacks == null) {
+            return;
+        }
+        for (Map.Entry<IBinder, IBackgroundActivityLaunchCallback> callbackEntry :
+                strictModeBalCallbacks.entrySet()) {
+            try {
+                callbackEntry.getValue().onBackgroundActivityLaunchAborted(message);
+            } catch (RemoteException e) {
+                removeStrictModeCallback(callingUid, callbackEntry.getKey());
+            }
+        }
     }
 
     /**
@@ -1069,7 +1192,7 @@
 
         // if the realCallingUid is a persistent system process, abort if the IntentSender
         // wasn't allowed to start an activity
-        if (state.mForcedBalByPiSender.allowsBackgroundActivityStarts()
+        if (state.mAllowBalExemptionForSystemProcess
                 && state.mIsRealCallingUidPersistentSystemProcess) {
             return new BalVerdict(BAL_ALLOW_ALLOWLISTED_UID,
                     /*background*/ false,
@@ -1385,7 +1508,7 @@
 
         String packageName =  mService.mContext.getPackageManager().getNameForUid(callingUid);
         BalState state = new BalState(callingUid, callingPid, packageName, INVALID_UID,
-                INVALID_PID, null, null, null, null, null, ActivityOptions.makeBasic());
+                INVALID_PID, null, null, false, null, null, ActivityOptions.makeBasic());
         @BalCode int balCode = checkBackgroundActivityStartAllowedByCaller(state).mCode;
         if (balCode == BAL_ALLOW_ALLOWLISTED_UID
                 || balCode == BAL_ALLOW_ALLOWLISTED_COMPONENT
diff --git a/services/core/java/com/android/server/wm/Dimmer.java b/services/core/java/com/android/server/wm/Dimmer.java
index 4824c16..9f40bed 100644
--- a/services/core/java/com/android/server/wm/Dimmer.java
+++ b/services/core/java/com/android/server/wm/Dimmer.java
@@ -261,6 +261,14 @@
         }
     }
 
+    boolean hasDimState() {
+        return mDimState != null;
+    }
+
+    boolean isDimming() {
+        return mDimState != null && mDimState.isDimming();
+    }
+
     @NonNull
     private DimState obtainDimState(@NonNull WindowState window) {
         if (mDimState == null) {
@@ -276,7 +284,6 @@
         return mDimState != null ? mDimState.mDimSurface : null;
     }
 
-    @Deprecated
     Rect getDimBounds() {
         return mDimState != null ? mDimState.mDimBounds : null;
     }
diff --git a/services/core/java/com/android/server/wm/DimmerAnimationHelper.java b/services/core/java/com/android/server/wm/DimmerAnimationHelper.java
index 3999e03..0d0e548 100644
--- a/services/core/java/com/android/server/wm/DimmerAnimationHelper.java
+++ b/services/core/java/com/android/server/wm/DimmerAnimationHelper.java
@@ -27,11 +27,13 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.graphics.Rect;
 import android.util.Log;
 import android.util.proto.ProtoOutputStream;
 import android.view.SurfaceControl;
 
 import com.android.internal.protolog.ProtoLog;
+import com.android.window.flags.Flags;
 
 import java.io.PrintWriter;
 
@@ -153,6 +155,9 @@
                         ? mRequestedProperties.mGeometryParent.getSurfaceControl() : null,
                 mRequestedProperties.mDimmingContainer != startProperties.mDimmingContainer
                         ? mRequestedProperties.mDimmingContainer.getSurfaceControl() : null, t);
+        if (Flags.useTasksDimOnly()) {
+            setBounds(dim, mCurrentProperties.mDimmingContainer, t);
+        }
 
         if (!startProperties.hasSameVisualProperties(mRequestedProperties)) {
             stopCurrentAnimation(dim.mDimSurface);
@@ -253,6 +258,32 @@
         }
     }
 
+    static void setBounds(@NonNull Dimmer.DimState dim, @NonNull WindowState relativeParent,
+                          @NonNull SurfaceControl.Transaction t) {
+        TaskFragment taskFragment = relativeParent.getTaskFragment();
+        Rect taskFragmentBounds = taskFragment != null ? taskFragment.getBounds() : null;
+        Task task = relativeParent.getTask();
+        Rect taskBounds = task != null ? task.getBounds() : null;
+        Rect hostBounds = dim.mHostContainer.getBounds();
+        boolean isEmbedded = taskFragment != null && taskFragment.isEmbedded();
+
+        Rect relativeBounds = new Rect();
+        if (isEmbedded) {
+            // Embedded activities can be dimmed at task or fragment level
+            dim.mDimBounds.set(taskFragment.isDimmingOnParentTask()
+                    ? taskBounds : taskFragmentBounds);
+            relativeBounds.set(dim.mDimBounds);
+            relativeBounds.offset(-taskBounds.left, -taskBounds.top);
+        } else {
+            dim.mDimBounds.set(hostBounds);
+            relativeBounds.set(dim.mDimBounds);
+            relativeBounds.offsetTo(0, 0);
+        }
+
+        t.setWindowCrop(dim.mDimSurface, relativeBounds.width(), relativeBounds.height());
+        t.setPosition(dim.mDimSurface, relativeBounds.left, relativeBounds.top);
+    }
+
     void setCurrentAlphaBlur(@NonNull Dimmer.DimState dim, @NonNull SurfaceControl.Transaction t) {
         final SurfaceControl sc = dim.mDimSurface;
         try {
diff --git a/services/core/java/com/android/server/wm/DisplayArea.java b/services/core/java/com/android/server/wm/DisplayArea.java
index 29ffda7..3b24798 100644
--- a/services/core/java/com/android/server/wm/DisplayArea.java
+++ b/services/core/java/com/android/server/wm/DisplayArea.java
@@ -46,6 +46,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.protolog.ProtoLog;
 import com.android.server.policy.WindowManagerPolicy;
+import com.android.window.flags.Flags;
 
 import java.io.PrintWriter;
 import java.util.Comparator;
@@ -53,6 +54,7 @@
 import java.util.function.Consumer;
 import java.util.function.Function;
 import java.util.function.Predicate;
+
 /**
  * Container for grouping WindowContainer below DisplayContent.
  *
@@ -831,11 +833,14 @@
         void prepareSurfaces() {
             mDimmer.resetDimStates();
             super.prepareSurfaces();
-            final Rect dimBounds = mDimmer.getDimBounds();
-            if (dimBounds != null) {
-                // Bounds need to be relative, as the dim layer is a child.
-                getBounds(dimBounds);
-                dimBounds.offsetTo(0 /* newLeft */, 0 /* newTop */);
+            Rect dimBounds = null;
+            if (!Flags.useTasksDimOnly()) {
+                dimBounds = mDimmer.getDimBounds();
+                if (dimBounds != null) {
+                    // Bounds need to be relative, as the dim layer is a child.
+                    getBounds(dimBounds);
+                    dimBounds.offsetTo(0 /* newLeft */, 0 /* newTop */);
+                }
             }
 
             // If SystemUI is dragging for recents, we want to reset the dim state so any dim layer
@@ -845,7 +850,7 @@
                 mDimmer.resetDimStates();
             }
 
-            if (dimBounds != null) {
+            if (mDimmer.hasDimState()) {
                 if (mDimmer.updateDims(getSyncTransaction())) {
                     scheduleAnimation();
                 }
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index d6ded51..aac756f 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -6923,27 +6923,25 @@
                 return;
             }
             if (mFixedRotationLaunchingApp.hasFixedRotationTransform(r)) {
+                if (!r.isVisible()) {
+                    // Let the opening activity update orientation.
+                    return;
+                }
                 if (mFixedRotationLaunchingApp.hasAnimatingFixedRotationTransition()) {
                     // Waiting until all of the associated activities have done animation, or the
                     // orientation would be updated too early and cause flickering.
                     return;
                 }
             } else {
-                // Handle a corner case that the task of {@link #mFixedRotationLaunchingApp} is no
-                // longer animating but the corresponding transition finished event won't notify.
-                // E.g. activity A transferred starting window to B, only A will receive transition
-                // finished event. A doesn't have fixed rotation but B is the rotated launching app.
-                final Task task = r.getTask();
-                if (task != mFixedRotationLaunchingApp.getTask()
+                // Check to skip updating display orientation by a non-top activity.
+                if ((!r.isVisible() || !mFixedRotationLaunchingApp.fillsParent())
                         // When closing a translucent task A (r.fillsParent() is false) to a
                         // visible task B, because only A has visibility change, there is only A's
                         // transition callback. Then it still needs to update orientation for B.
-                        && (!mWmService.mFlags.mRespectNonTopVisibleFixedOrientation
-                                || r.fillsParent())) {
-                    // Different tasks won't be in one activity transition animation.
+                        && r.fillsParent()) {
                     return;
                 }
-                if (task.getActivity(ActivityRecord::isInTransition) != null) {
+                if (r.inTransition()) {
                     return;
                     // Continue to update orientation because the transition of the top rotated
                     // launching activity is done.
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 5b2fecd..c7d57fe 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -17,7 +17,6 @@
 package com.android.server.wm;
 
 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
-import static android.inputmethodservice.InputMethodService.ENABLE_HIDE_IME_CAPTION_BAR;
 import static android.view.Display.TYPE_INTERNAL;
 import static android.view.InsetsFrameProvider.SOURCE_ARBITRARY_RECTANGLE;
 import static android.view.InsetsFrameProvider.SOURCE_CONTAINER_BOUNDS;
@@ -1248,17 +1247,7 @@
                 throw new IllegalArgumentException("IME insets must be provided by a window.");
             }
 
-            if (!ENABLE_HIDE_IME_CAPTION_BAR && mNavigationBar != null && mHasBottomNavigationBar) {
-                // In gesture navigation, nav bar frame is larger than frame to calculate insets.
-                // IME should not provide frame which is smaller than the nav bar frame. Otherwise,
-                // nav bar might be overlapped with the content of the client when IME is shown.
-                sTmpRect.set(inOutFrame);
-                sTmpRect.intersectUnchecked(mNavigationBar.getFrame());
-                inOutFrame.inset(windowState.mGivenContentInsets);
-                inOutFrame.union(sTmpRect);
-            } else {
-                inOutFrame.inset(windowState.mGivenContentInsets);
-            }
+            inOutFrame.inset(windowState.mGivenContentInsets);
             return 0;
         };
     }
diff --git a/services/core/java/com/android/server/wm/DragDropController.java b/services/core/java/com/android/server/wm/DragDropController.java
index c849a37..258a87e 100644
--- a/services/core/java/com/android/server/wm/DragDropController.java
+++ b/services/core/java/com/android/server/wm/DragDropController.java
@@ -156,7 +156,7 @@
             SurfaceControl surface, int touchSource, int touchDeviceId, int touchPointerId,
             float touchX, float touchY, float thumbCenterX, float thumbCenterY, ClipData data) {
         if (DEBUG_DRAG) {
-            Slog.d(TAG_WM, "perform drag: win=" + window + " surface=" + surface + " flags=" +
+            Slog.d(TAG_WM, "perform drag: win=" + window + " surface=" + surface + " flags=0x" +
                     Integer.toHexString(flags) + " data=" + data + " touch(" + touchX + ","
                     + touchY + ") thumb center(" + thumbCenterX + "," + thumbCenterY + ")");
         }
diff --git a/services/core/java/com/android/server/wm/KeyguardController.java b/services/core/java/com/android/server/wm/KeyguardController.java
index 2fde5aa..2664d8c 100644
--- a/services/core/java/com/android/server/wm/KeyguardController.java
+++ b/services/core/java/com/android/server/wm/KeyguardController.java
@@ -329,7 +329,7 @@
         // If the client has requested to dismiss the keyguard and the Activity has the flag to
         // turn the screen on, wakeup the screen if it's the top Activity.
         if (activityRecord.getTurnScreenOnFlag() && activityRecord.isTopRunningActivity()) {
-            mTaskSupervisor.wakeUp("dismissKeyguard");
+            mTaskSupervisor.wakeUp(activityRecord.getDisplayId(), "dismissKeyguard");
         }
 
         mWindowManager.dismissKeyguard(callback, message);
@@ -765,9 +765,9 @@
             }
 
             if (mTopTurnScreenOnActivity != null
-                    && !mService.mWindowManager.mPowerManager.isInteractive()
+                    && !mService.mWindowManager.mPowerManager.isInteractive(display.getDisplayId())
                     && (mRequestDismissKeyguard || mOccluded)) {
-                controller.mTaskSupervisor.wakeUp("handleTurnScreenOn");
+                controller.mTaskSupervisor.wakeUp(display.getDisplayId(), "handleTurnScreenOn");
                 mTopTurnScreenOnActivity.setCurrentLaunchCanTurnScreenOn(false);
             }
 
diff --git a/services/core/java/com/android/server/wm/OWNERS b/services/core/java/com/android/server/wm/OWNERS
index 5d6d8bc..e983edf 100644
--- a/services/core/java/com/android/server/wm/OWNERS
+++ b/services/core/java/com/android/server/wm/OWNERS
@@ -32,3 +32,7 @@
 # Files related to tracing
 per-file *TransitionTracer.java = file:platform/development:/tools/winscope/OWNERS
 per-file *WindowTracing* = file:platform/development:/tools/winscope/OWNERS
+
+# Files related to activity security
+per-file ActivityStarter.java = file:/ACTIVITY_SECURITY_OWNERS
+per-file ActivityTaskManagerService.java = file:/ACTIVITY_SECURITY_OWNERS
diff --git a/services/core/java/com/android/server/wm/RecentsAnimation.java b/services/core/java/com/android/server/wm/RecentsAnimation.java
index 0b13671..0c795ea 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimation.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimation.java
@@ -31,6 +31,7 @@
 import android.app.ActivityOptions;
 import android.content.ComponentName;
 import android.content.Intent;
+import android.os.Binder;
 import android.util.Slog;
 
 import com.android.internal.protolog.ProtoLog;
@@ -154,7 +155,8 @@
                 .setCallingUid(mRecentsUid)
                 .setCallingPackage(mRecentsComponent.getPackageName())
                 .setCallingFeatureId(mRecentsFeatureId)
-                .setActivityOptions(new SafeActivityOptions(options))
+                .setActivityOptions(new SafeActivityOptions(options,
+                        Binder.getCallingPid(), Binder.getCallingUid()))
                 .setUserId(mUserId)
                 .execute();
     }
diff --git a/services/core/java/com/android/server/wm/SafeActivityOptions.java b/services/core/java/com/android/server/wm/SafeActivityOptions.java
index 8c7b637..70b214c 100644
--- a/services/core/java/com/android/server/wm/SafeActivityOptions.java
+++ b/services/core/java/com/android/server/wm/SafeActivityOptions.java
@@ -70,19 +70,6 @@
     private @Nullable ActivityOptions mCallerOptions;
 
     /**
-     * Constructs a new instance from a bundle and records {@link Binder#getCallingPid}/
-     * {@link Binder#getCallingUid}. Thus, calling identity MUST NOT be cleared when constructing
-     * this object.
-     *
-     * @param bOptions The {@link ActivityOptions} as {@link Bundle}.
-     */
-    public static SafeActivityOptions fromBundle(Bundle bOptions) {
-        return bOptions != null
-                ? new SafeActivityOptions(ActivityOptions.fromBundle(bOptions))
-                : null;
-    }
-
-    /**
      * Constructs a new instance from a bundle and provided pid/uid.
      *
      * @param bOptions The {@link ActivityOptions} as {@link Bundle}.
@@ -95,24 +82,11 @@
     }
 
     /**
-     * Constructs a new instance and records {@link Binder#getCallingPid}/
-     * {@link Binder#getCallingUid}. Thus, calling identity MUST NOT be cleared when constructing
-     * this object.
-     *
-     * @param options The options to wrap.
-     */
-    public SafeActivityOptions(@Nullable ActivityOptions options) {
-        mOriginalCallingPid = Binder.getCallingPid();
-        mOriginalCallingUid = Binder.getCallingUid();
-        mOriginalOptions = options;
-    }
-
-    /**
      * Constructs a new instance.
      *
      * @param options The options to wrap.
      */
-    private SafeActivityOptions(@Nullable ActivityOptions options, int callingPid, int callingUid) {
+    public SafeActivityOptions(@Nullable ActivityOptions options, int callingPid, int callingUid) {
         mOriginalCallingPid = callingPid;
         mOriginalCallingUid = callingUid;
         mOriginalOptions = options;
@@ -158,12 +132,12 @@
 
     /**
      * Overrides options with options from a caller and records {@link Binder#getCallingPid}/
-     * {@link Binder#getCallingUid}. Thus, calling identity MUST NOT be cleared when calling this
-     * method.
+     * {@link Binder#getCallingUid}.
      */
-    public void setCallerOptions(@Nullable ActivityOptions options) {
-        mRealCallingPid = Binder.getCallingPid();
-        mRealCallingUid = Binder.getCallingUid();
+    public void setCallerOptions(@Nullable ActivityOptions options, int callingPid,
+            int callingUid) {
+        mRealCallingPid = callingPid;
+        mRealCallingUid = callingUid;
         mCallerOptions = options;
     }
 
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 8a624b3..b14dd3f 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -3297,22 +3297,25 @@
         mDimmer.resetDimStates();
         super.prepareSurfaces();
 
-        final Rect dimBounds = mDimmer.getDimBounds();
-        if (dimBounds != null) {
-            getDimBounds(dimBounds);
+        Rect dimBounds = null;
+        if (!Flags.useTasksDimOnly()) {
+            dimBounds = mDimmer.getDimBounds();
+            if (dimBounds != null) {
+                getDimBounds(dimBounds);
 
-            // Bounds need to be relative, as the dim layer is a child.
-            if (inFreeformWindowingMode()) {
-                getBounds(mTmpRect);
-                dimBounds.offsetTo(dimBounds.left - mTmpRect.left, dimBounds.top - mTmpRect.top);
-            } else {
-                dimBounds.offsetTo(0, 0);
+                // Bounds need to be relative, as the dim layer is a child.
+                if (inFreeformWindowingMode()) {
+                    getBounds(mTmpRect);
+                    dimBounds.offset(-mTmpRect.left, -mTmpRect.top);
+                } else {
+                    dimBounds.offsetTo(0, 0);
+                }
             }
         }
 
         final SurfaceControl.Transaction t = getSyncTransaction();
 
-        if (dimBounds != null && mDimmer.updateDims(t)) {
+        if (mDimmer.hasDimState() && mDimmer.updateDims(t)) {
             scheduleAnimation();
         }
 
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index d6ba312..606d51d 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -3157,12 +3157,16 @@
 
     /** Bounds to be used for dimming, as well as touch related tests. */
     void getDimBounds(@NonNull Rect out) {
-        if (mIsEmbedded && isDimmingOnParentTask() && getDimmer().getDimBounds() != null) {
-            // Return the task bounds if the dimmer is showing and should cover on the Task (not
-            // just on this embedded TaskFragment).
-            out.set(getTask().getBounds());
+        if (Flags.useTasksDimOnly() && mDimmer.hasDimState()) {
+            out.set(mDimmer.getDimBounds());
         } else {
-            out.set(getBounds());
+            if (mIsEmbedded && isDimmingOnParentTask() && getDimmer().getDimBounds() != null) {
+                // Return the task bounds if the dimmer is showing and should cover on the Task (not
+                // just on this embedded TaskFragment).
+                out.set(getTask().getBounds());
+            } else {
+                out.set(getBounds());
+            }
         }
     }
 
@@ -3193,10 +3197,16 @@
         mDimmer.resetDimStates();
         super.prepareSurfaces();
 
-        final Rect dimBounds = mDimmer.getDimBounds();
-        if (dimBounds != null) {
-            // Bounds need to be relative, as the dim layer is a child.
-            dimBounds.offsetTo(0 /* newLeft */, 0 /* newTop */);
+        if (!Flags.useTasksDimOnly()) {
+            final Rect dimBounds = mDimmer.getDimBounds();
+            if (dimBounds != null) {
+                // Bounds need to be relative, as the dim layer is a child.
+                dimBounds.offsetTo(0 /* newLeft */, 0 /* newTop */);
+                if (mDimmer.updateDims(getSyncTransaction())) {
+                    scheduleAnimation();
+                }
+            }
+        } else {
             if (mDimmer.updateDims(getSyncTransaction())) {
                 scheduleAnimation();
             }
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 585537b..fc46600 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -557,6 +557,14 @@
         });
     }
 
+    /** Set a transition to be a back gesture animation. */
+    void setBackGestureAnimation(@NonNull WindowContainer wc, boolean isTop) {
+        final ChangeInfo info = mChanges.get(wc);
+        if (info == null) return;
+        info.mFlags = info.mFlags | (isTop ? ChangeInfo.FLAG_BACK_GESTURE_ANIMATION
+                : ChangeInfo.FLAG_BELOW_BACK_GESTURE_ANIMATION);
+    }
+
     /** Set a transition to be a seamless-rotation. */
     void setSeamlessRotation(@NonNull WindowContainer wc) {
         final ChangeInfo info = mChanges.get(wc);
@@ -3405,6 +3413,18 @@
         /** Whether this change contains config-at-end members. */
         private static final int FLAG_CHANGE_CONFIG_AT_END = 0x40;
 
+        /**
+         * Whether this change is forced participant transition because it is current top target
+         * of predictive back animation.
+         */
+        private static final int FLAG_BACK_GESTURE_ANIMATION = 0x80;
+
+        /**
+         * Whether this change is forced participant transition because it is previous target of
+         * predictive back animation
+         */
+        private static final int FLAG_BELOW_BACK_GESTURE_ANIMATION = 0x100;
+
         @IntDef(prefix = { "FLAG_" }, value = {
                 FLAG_NONE,
                 FLAG_SEAMLESS_ROTATION,
@@ -3413,7 +3433,9 @@
                 FLAG_CHANGE_NO_ANIMATION,
                 FLAG_CHANGE_YES_ANIMATION,
                 FLAG_CHANGE_MOVED_TO_TOP,
-                FLAG_CHANGE_CONFIG_AT_END
+                FLAG_CHANGE_CONFIG_AT_END,
+                FLAG_BACK_GESTURE_ANIMATION,
+                FLAG_BELOW_BACK_GESTURE_ANIMATION
         })
         @Retention(RetentionPolicy.SOURCE)
         @interface Flag {}
@@ -3487,7 +3509,9 @@
             final boolean currVisible = mContainer.isVisibleRequested();
             // the task including transient launch must promote to root task
             if (currVisible && ((mFlags & ChangeInfo.FLAG_TRANSIENT_LAUNCH) != 0
-                    || (mFlags & ChangeInfo.FLAG_ABOVE_TRANSIENT_LAUNCH) != 0)) {
+                    || (mFlags & ChangeInfo.FLAG_ABOVE_TRANSIENT_LAUNCH) != 0)
+                    || (mFlags & ChangeInfo.FLAG_BACK_GESTURE_ANIMATION) != 0
+                    || (mFlags & ChangeInfo.FLAG_BELOW_BACK_GESTURE_ANIMATION) != 0) {
                 return true;
             }
             // If it's invisible and hasn't changed visibility, always return false since even if
@@ -3509,6 +3533,9 @@
             if ((mFlags & ChangeInfo.FLAG_ABOVE_TRANSIENT_LAUNCH) != 0) {
                 return mExistenceChanged ? TRANSIT_CLOSE : TRANSIT_TO_BACK;
             }
+            if ((mFlags & ChangeInfo.FLAG_BELOW_BACK_GESTURE_ANIMATION) != 0) {
+                return TRANSIT_TO_FRONT;
+            }
             final boolean nowVisible = wc.isVisibleRequested();
             if (nowVisible == mVisible) {
                 return TRANSIT_CHANGE;
@@ -3540,7 +3567,8 @@
                             && topActivity.mStartingData.hasImeSurface()) {
                         flags |= FLAG_WILL_IME_SHOWN;
                     }
-                    if (topActivity.mLaunchTaskBehind) {
+                    if (topActivity.mLaunchTaskBehind
+                            && !topActivity.isAnimating(PARENTS, ANIMATION_TYPE_PREDICT_BACK)) {
                         Slog.e(TAG, "Unexpected launch-task-behind operation in shell transition");
                         flags |= FLAG_TASK_LAUNCHING_BEHIND;
                     }
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 95cf6bc..6c92ae6 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -1117,6 +1117,7 @@
      */
     void onDisplayChanged(DisplayContent dc) {
         if (mDisplayContent != null && mDisplayContent != dc) {
+            mTransitionController.collect(this);
             // Cancel any change transition queued-up for this container on the old display when
             // this container is moved from the old display.
             mDisplayContent.mClosingChangingContainers.remove(this);
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index f4ad030..97bf587 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -3421,9 +3421,11 @@
             throw new SecurityException("Requires CONTROL_KEYGUARD permission");
         }
         synchronized (mGlobalLock) {
+            final DisplayContent defaultDisplayContent = getDefaultDisplayContentLocked();
             if (!dreamHandlesConfirmKeys()
-                    && getDefaultDisplayContentLocked().getDisplayPolicy().isShowingDreamLw()) {
-                mAtmService.mTaskSupervisor.wakeUp("leaveDream");
+                    && defaultDisplayContent.getDisplayPolicy().isShowingDreamLw()) {
+                mAtmService.mTaskSupervisor.wakeUp(
+                        defaultDisplayContent.getDisplayId(), "leaveDream");
             }
             mPolicy.dismissKeyguardLw(callback, message);
         }
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 6141876..585e617 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -2862,12 +2862,13 @@
 
             if (allowTheaterMode && canTurnScreenOn
                     && (mWmService.mAtmService.isDreaming()
-                            || !mWmService.mPowerManager.isInteractive())) {
+                            || !mWmService.mPowerManager.isInteractive(getDisplayId()))) {
                 if (DEBUG_VISIBILITY || DEBUG_POWER) {
                     Slog.v(TAG, "Relayout window turning screen on: " + this);
                 }
                 mWmService.mPowerManager.wakeUp(SystemClock.uptimeMillis(),
-                        PowerManager.WAKE_REASON_APPLICATION, "android.server.wm:SCREEN_ON_FLAG");
+                        PowerManager.WAKE_REASON_APPLICATION, "android.server.wm:SCREEN_ON_FLAG",
+                        getDisplayId());
             }
 
             if (mActivityRecord != null) {
diff --git a/services/core/jni/com_android_server_vibrator_VibratorController.cpp b/services/core/jni/com_android_server_vibrator_VibratorController.cpp
index 903d892..59dbf28 100644
--- a/services/core/jni/com_android_server_vibrator_VibratorController.cpp
+++ b/services/core/jni/com_android_server_vibrator_VibratorController.cpp
@@ -73,6 +73,13 @@
     jfieldID endFrequencyHz;
     jfieldID duration;
 } sRampClassInfo;
+static struct {
+    jfieldID startAmplitude;
+    jfieldID endAmplitude;
+    jfieldID startFrequencyHz;
+    jfieldID endFrequencyHz;
+    jfieldID duration;
+} sPwleClassInfo;
 
 static_assert(static_cast<uint8_t>(V1_0::EffectStrength::LIGHT) ==
               static_cast<uint8_t>(Aidl::EffectStrength::LIGHT));
@@ -182,6 +189,15 @@
     return pwle;
 }
 
+static Aidl::PwleV2Primitive pwleV2PrimitiveFromJavaPrimitive(JNIEnv* env, jobject pwleObj) {
+    Aidl::PwleV2Primitive pwle;
+    pwle.amplitude = static_cast<float>(env->GetFloatField(pwleObj, sPwleClassInfo.endAmplitude));
+    pwle.frequencyHz =
+            static_cast<float>(env->GetFloatField(pwleObj, sPwleClassInfo.endFrequencyHz));
+    pwle.timeMillis = static_cast<int32_t>(env->GetIntField(pwleObj, sPwleClassInfo.duration));
+    return pwle;
+}
+
 /* Return true if braking is not NONE and the active PWLE starts and ends with zero amplitude. */
 static bool shouldBeReplacedWithBraking(Aidl::ActivePwle activePwle, Aidl::Braking braking) {
     return (braking != Aidl::Braking::NONE) && (activePwle.startAmplitude == 0) &&
@@ -399,6 +415,31 @@
     return result.isOk() ? totalDuration.count() : (result.isUnsupported() ? 0 : -1);
 }
 
+static jlong vibratorPerformPwleV2Effect(JNIEnv* env, jclass /* clazz */, jlong ptr,
+                                         jobjectArray waveform, jlong vibrationId) {
+    VibratorControllerWrapper* wrapper = reinterpret_cast<VibratorControllerWrapper*>(ptr);
+    if (wrapper == nullptr) {
+        ALOGE("vibratorPerformPwleV2Effect failed because native wrapper was not initialized");
+        return -1;
+    }
+    size_t size = env->GetArrayLength(waveform);
+    Aidl::CompositePwleV2 composite;
+    std::vector<Aidl::PwleV2Primitive> primitives;
+    for (size_t i = 0; i < size; i++) {
+        jobject element = env->GetObjectArrayElement(waveform, i);
+        Aidl::PwleV2Primitive pwle = pwleV2PrimitiveFromJavaPrimitive(env, element);
+        primitives.push_back(pwle);
+    }
+    composite.pwlePrimitives = primitives;
+
+    auto callback = wrapper->createCallback(vibrationId);
+    auto composePwleV2Fn = [&composite, &callback](vibrator::HalWrapper* hal) {
+        return hal->composePwleV2(composite, callback);
+    };
+    auto result = wrapper->halCall<void>(composePwleV2Fn, "composePwleV2");
+    return result.isOk();
+}
+
 static void vibratorAlwaysOnEnable(JNIEnv* env, jclass /* clazz */, jlong ptr, jlong id,
                                    jlong effect, jlong strength) {
     VibratorControllerWrapper* wrapper = reinterpret_cast<VibratorControllerWrapper*>(ptr);
@@ -579,6 +620,8 @@
          (void*)vibratorPerformComposedEffect},
         {"performPwleEffect", "(J[Landroid/os/vibrator/RampSegment;IJ)J",
          (void*)vibratorPerformPwleEffect},
+        {"performPwleV2Effect", "(J[Landroid/os/vibrator/PwleSegment;J)J",
+         (void*)vibratorPerformPwleV2Effect},
         {"setExternalControl", "(JZ)V", (void*)vibratorSetExternalControl},
         {"alwaysOnEnable", "(JJJJ)V", (void*)vibratorAlwaysOnEnable},
         {"alwaysOnDisable", "(JJ)V", (void*)vibratorAlwaysOnDisable},
@@ -604,6 +647,13 @@
     sRampClassInfo.endFrequencyHz = GetFieldIDOrDie(env, rampClass, "mEndFrequencyHz", "F");
     sRampClassInfo.duration = GetFieldIDOrDie(env, rampClass, "mDuration", "I");
 
+    jclass pwleClass = FindClassOrDie(env, "android/os/vibrator/PwleSegment");
+    sPwleClassInfo.startAmplitude = GetFieldIDOrDie(env, pwleClass, "mStartAmplitude", "F");
+    sPwleClassInfo.endAmplitude = GetFieldIDOrDie(env, pwleClass, "mEndAmplitude", "F");
+    sPwleClassInfo.startFrequencyHz = GetFieldIDOrDie(env, pwleClass, "mStartFrequencyHz", "F");
+    sPwleClassInfo.endFrequencyHz = GetFieldIDOrDie(env, pwleClass, "mEndFrequencyHz", "F");
+    sPwleClassInfo.duration = GetFieldIDOrDie(env, pwleClass, "mDuration", "I");
+
     jclass frequencyProfileLegacyClass =
             FindClassOrDie(env, "android/os/VibratorInfo$FrequencyProfileLegacy");
     sFrequencyProfileLegacyClass =
diff --git a/services/core/lint-baseline.xml b/services/core/lint-baseline.xml
index 3b81f0a..4c1ac39 100644
--- a/services/core/lint-baseline.xml
+++ b/services/core/lint-baseline.xml
@@ -178,4 +178,675 @@
             column="51"/>
     </issue>
 
-</issues>
\ No newline at end of file
+    <issue
+        id="MissingPermissionAnnotation"
+        message="onShellCommand should be annotated with either @EnforcePermission, @RequiresNoPermission or @PermissionManuallyEnforced."
+        errorLine1="    @Override"
+        errorLine2="    ^">
+        <location
+            file="frameworks/base/services/core/java/com/android/server/security/advancedprotection/AdvancedProtectionService.java"
+            line="128"
+            column="5"/>
+    </issue>
+
+    <issue
+        id="MissingPermissionAnnotation"
+        message="monitorState should be annotated with either @EnforcePermission, @RequiresNoPermission or @PermissionManuallyEnforced."
+        errorLine1="        @Override"
+        errorLine2="        ^">
+        <location
+            file="frameworks/base/services/core/java/com/android/server/security/forensic/ForensicService.java"
+            line="95"
+            column="9"/>
+    </issue>
+
+    <issue
+        id="MissingPermissionAnnotation"
+        message="makeVisible should be annotated with either @EnforcePermission, @RequiresNoPermission or @PermissionManuallyEnforced."
+        errorLine1="        @Override"
+        errorLine2="        ^">
+        <location
+            file="frameworks/base/services/core/java/com/android/server/security/forensic/ForensicService.java"
+            line="100"
+            column="9"/>
+    </issue>
+
+    <issue
+        id="MissingPermissionAnnotation"
+        message="makeInvisible should be annotated with either @EnforcePermission, @RequiresNoPermission or @PermissionManuallyEnforced."
+        errorLine1="        @Override"
+        errorLine2="        ^">
+        <location
+            file="frameworks/base/services/core/java/com/android/server/security/forensic/ForensicService.java"
+            line="105"
+            column="9"/>
+    </issue>
+
+    <issue
+        id="MissingPermissionAnnotation"
+        message="enable should be annotated with either @EnforcePermission, @RequiresNoPermission or @PermissionManuallyEnforced."
+        errorLine1="        @Override"
+        errorLine2="        ^">
+        <location
+            file="frameworks/base/services/core/java/com/android/server/security/forensic/ForensicService.java"
+            line="110"
+            column="9"/>
+    </issue>
+
+    <issue
+        id="MissingPermissionAnnotation"
+        message="disable should be annotated with either @EnforcePermission, @RequiresNoPermission or @PermissionManuallyEnforced."
+        errorLine1="        @Override"
+        errorLine2="        ^">
+        <location
+            file="frameworks/base/services/core/java/com/android/server/security/forensic/ForensicService.java"
+            line="115"
+            column="9"/>
+    </issue>
+
+    <issue
+        id="MissingPermissionAnnotation"
+        message="getTimeoutTime should be annotated with either @EnforcePermission, @RequiresNoPermission or @PermissionManuallyEnforced."
+        errorLine1="        @Override"
+        errorLine2="        ^">
+        <location
+            file="frameworks/base/services/core/java/com/android/server/pm/verify/pkg/VerifierController.java"
+            line="430"
+            column="9"/>
+    </issue>
+
+    <issue
+        id="MissingPermissionAnnotation"
+        message="extendTimeRemaining should be annotated with either @EnforcePermission, @RequiresNoPermission or @PermissionManuallyEnforced."
+        errorLine1="        @Override"
+        errorLine2="        ^">
+        <location
+            file="frameworks/base/services/core/java/com/android/server/pm/verify/pkg/VerifierController.java"
+            line="443"
+            column="9"/>
+    </issue>
+
+    <issue
+        id="MissingPermissionAnnotation"
+        message="setVerificationPolicy should be annotated with either @EnforcePermission, @RequiresNoPermission or @PermissionManuallyEnforced."
+        errorLine1="        @Override"
+        errorLine2="        ^">
+        <location
+            file="frameworks/base/services/core/java/com/android/server/pm/verify/pkg/VerifierController.java"
+            line="456"
+            column="9"/>
+    </issue>
+
+    <issue
+        id="MissingPermissionAnnotation"
+        message="reportVerificationIncomplete should be annotated with either @EnforcePermission, @RequiresNoPermission or @PermissionManuallyEnforced."
+        errorLine1="        @Override"
+        errorLine2="        ^">
+        <location
+            file="frameworks/base/services/core/java/com/android/server/pm/verify/pkg/VerifierController.java"
+            line="470"
+            column="9"/>
+    </issue>
+
+    <issue
+        id="MissingPermissionAnnotation"
+        message="reportVerificationComplete should be annotated with either @EnforcePermission, @RequiresNoPermission or @PermissionManuallyEnforced."
+        errorLine1="        @Override"
+        errorLine2="        ^">
+        <location
+            file="frameworks/base/services/core/java/com/android/server/pm/verify/pkg/VerifierController.java"
+            line="486"
+            column="9"/>
+    </issue>
+
+    <issue
+        id="MissingPermissionAnnotation"
+        message="reportVerificationCompleteWithExtensionResponse should be annotated with either @EnforcePermission, @RequiresNoPermission or @PermissionManuallyEnforced."
+        errorLine1="        @Override"
+        errorLine2="        ^">
+        <location
+            file="frameworks/base/services/core/java/com/android/server/pm/verify/pkg/VerifierController.java"
+            line="492"
+            column="9"/>
+    </issue>
+
+    <issue
+        id="SimpleManualPermissionEnforcement"
+        message="IActivityClientController permission check can be converted to @EnforcePermission annotation"
+        errorLine1="        mService.mAmInternal.enforceCallingPermission("
+        errorLine2="        ^">
+        <location
+            file="out/soong/.intermediates/frameworks/base/services/core/services.core.protologsrc/gen/services.core.protolog.srcjar!/frameworks/base/services/core/java/com/android/server/wm/ActivityClientController.java"
+            line="592"
+            column="9"/>
+    </issue>
+
+    <issue
+        id="SimpleManualPermissionEnforcement"
+        message="IActivityClientController permission check can be converted to @EnforcePermission annotation"
+        errorLine1="        mService.mAmInternal.enforceCallingPermission(CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS,"
+        errorLine2="        ^">
+        <location
+            file="out/soong/.intermediates/frameworks/base/services/core/services.core.protologsrc/gen/services.core.protolog.srcjar!/frameworks/base/services/core/java/com/android/server/wm/ActivityClientController.java"
+            line="1636"
+            column="9"/>
+    </issue>
+
+    <issue
+        id="SimpleManualPermissionEnforcement"
+        message="IActivityClientController permission check can be converted to @EnforcePermission annotation"
+        errorLine1="        mService.mAmInternal.enforceCallingPermission(CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS,"
+        errorLine2="        ^">
+        <location
+            file="out/soong/.intermediates/frameworks/base/services/core/services.core.protologsrc/gen/services.core.protolog.srcjar!/frameworks/base/services/core/java/com/android/server/wm/ActivityClientController.java"
+            line="1654"
+            column="9"/>
+    </issue>
+
+    <issue
+        id="SimpleManualPermissionEnforcement"
+        message="IActivityTaskManager permission check can be converted to @EnforcePermission annotation"
+        errorLine1="        mAmInternal.enforceCallingPermission(START_TASKS_FROM_RECENTS,"
+        errorLine2="        ^">
+        <location
+            file="out/soong/.intermediates/frameworks/base/services/core/services.core.protologsrc/gen/services.core.protolog.srcjar!/frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java"
+            line="1820"
+            column="9"/>
+    </issue>
+
+    <issue
+        id="SimpleManualPermissionEnforcement"
+        message="IActivityTaskManager permission check can be converted to @EnforcePermission annotation"
+        errorLine1="        mAmInternal.enforceCallingPermission(START_TASKS_FROM_RECENTS,"
+        errorLine2="        ^">
+        <location
+            file="out/soong/.intermediates/frameworks/base/services/core/services.core.protologsrc/gen/services.core.protolog.srcjar!/frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java"
+            line="1875"
+            column="9"/>
+    </issue>
+
+    <issue
+        id="SimpleManualPermissionEnforcement"
+        message="IActivityTaskManager permission check can be converted to @EnforcePermission annotation"
+        errorLine1="        mAmInternal.enforceCallingPermission(android.Manifest.permission.SET_SCREEN_COMPATIBILITY,"
+        errorLine2="        ^">
+        <location
+            file="out/soong/.intermediates/frameworks/base/services/core/services.core.protologsrc/gen/services.core.protolog.srcjar!/frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java"
+            line="1980"
+            column="9"/>
+    </issue>
+
+    <issue
+        id="SimpleManualPermissionEnforcement"
+        message="IActivityTaskManager permission check can be converted to @EnforcePermission annotation"
+        errorLine1="        mAmInternal.enforceCallingPermission(REMOVE_TASKS, &quot;removeTask()&quot;);"
+        errorLine2="        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="out/soong/.intermediates/frameworks/base/services/core/services.core.protologsrc/gen/services.core.protolog.srcjar!/frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java"
+            line="2116"
+            column="9"/>
+    </issue>
+
+    <issue
+        id="SimpleManualPermissionEnforcement"
+        message="IActivityTaskManager permission check can be converted to @EnforcePermission annotation"
+        errorLine1="        mAmInternal.enforceCallingPermission(REMOVE_TASKS, &quot;removeAllVisibleRecentTasks()&quot;);"
+        errorLine2="        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="out/soong/.intermediates/frameworks/base/services/core/services.core.protologsrc/gen/services.core.protolog.srcjar!/frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java"
+            line="2144"
+            column="9"/>
+    </issue>
+
+    <issue
+        id="SimpleManualPermissionEnforcement"
+        message="IActivityTaskManager permission check can be converted to @EnforcePermission annotation"
+        errorLine1="        mAmInternal.enforceCallingPermission(android.Manifest.permission.FORCE_BACK,"
+        errorLine2="        ^">
+        <location
+            file="out/soong/.intermediates/frameworks/base/services/core/services.core.protologsrc/gen/services.core.protolog.srcjar!/frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java"
+            line="2206"
+            column="9"/>
+    </issue>
+
+    <issue
+        id="SimpleManualPermissionEnforcement"
+        message="IActivityTaskManager permission check can be converted to @EnforcePermission annotation"
+        errorLine1="        mAmInternal.enforceCallingPermission(android.Manifest.permission.REORDER_TASKS,"
+        errorLine2="        ^">
+        <location
+            file="out/soong/.intermediates/frameworks/base/services/core/services.core.protologsrc/gen/services.core.protolog.srcjar!/frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java"
+            line="2228"
+            column="9"/>
+    </issue>
+
+    <issue
+        id="SimpleManualPermissionEnforcement"
+        message="IActivityTaskManager permission check can be converted to @EnforcePermission annotation"
+        errorLine1="        mAmInternal.enforceCallingPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER,"
+        errorLine2="        ^">
+        <location
+            file="out/soong/.intermediates/frameworks/base/services/core/services.core.protologsrc/gen/services.core.protolog.srcjar!/frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java"
+            line="2371"
+            column="9"/>
+    </issue>
+
+    <issue
+        id="SimpleManualPermissionEnforcement"
+        message="IActivityTaskManager permission check can be converted to @EnforcePermission annotation"
+        errorLine1="        mAmInternal.enforceCallingPermission(INTERNAL_SYSTEM_WINDOW, &quot;moveRootTaskToDisplay()&quot;);"
+        errorLine2="        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="out/soong/.intermediates/frameworks/base/services/core/services.core.protologsrc/gen/services.core.protolog.srcjar!/frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java"
+            line="3103"
+            column="9"/>
+    </issue>
+
+    <issue
+        id="SimpleManualPermissionEnforcement"
+        message="IActivityTaskManager permission check can be converted to @EnforcePermission annotation"
+        errorLine1="        mAmInternal.enforceCallingPermission(android.Manifest.permission.GET_TOP_ACTIVITY_INFO,"
+        errorLine2="        ^">
+        <location
+            file="out/soong/.intermediates/frameworks/base/services/core/services.core.protologsrc/gen/services.core.protolog.srcjar!/frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java"
+            line="3157"
+            column="9"/>
+    </issue>
+
+    <issue
+        id="SimpleManualPermissionEnforcement"
+        message="IActivityTaskManager permission check can be converted to @EnforcePermission annotation"
+        errorLine1="        mAmInternal.enforceCallingPermission(CONTROL_KEYGUARD, &quot;unlock keyguard&quot;);"
+        errorLine2="        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="out/soong/.intermediates/frameworks/base/services/core/services.core.protologsrc/gen/services.core.protolog.srcjar!/frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java"
+            line="3640"
+            column="9"/>
+    </issue>
+
+    <issue
+        id="SimpleManualPermissionEnforcement"
+        message="IActivityTaskManager permission check can be converted to @EnforcePermission annotation"
+        errorLine1="        mAmInternal.enforceCallingPermission(MANAGE_ACTIVITY_TASKS,"
+        errorLine2="        ^">
+        <location
+            file="out/soong/.intermediates/frameworks/base/services/core/services.core.protologsrc/gen/services.core.protolog.srcjar!/frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java"
+            line="3683"
+            column="9"/>
+    </issue>
+
+    <issue
+        id="SimpleManualPermissionEnforcement"
+        message="IActivityTaskManager permission check can be converted to @EnforcePermission annotation"
+        errorLine1="        mAmInternal.enforceCallingPermission(MANAGE_ACTIVITY_TASKS,"
+        errorLine2="        ^">
+        <location
+            file="out/soong/.intermediates/frameworks/base/services/core/services.core.protologsrc/gen/services.core.protolog.srcjar!/frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java"
+            line="3701"
+            column="9"/>
+    </issue>
+
+    <issue
+        id="SimpleManualPermissionEnforcement"
+        message="IActivityTaskManager permission check can be converted to @EnforcePermission annotation"
+        errorLine1="        mAmInternal.enforceCallingPermission(CHANGE_CONFIGURATION, &quot;updateConfiguration()&quot;);"
+        errorLine2="        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="out/soong/.intermediates/frameworks/base/services/core/services.core.protologsrc/gen/services.core.protolog.srcjar!/frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java"
+            line="3904"
+            column="9"/>
+    </issue>
+
+    <issue
+        id="SimpleManualPermissionEnforcement"
+        message="IActivityTaskManager permission check can be converted to @EnforcePermission annotation"
+        errorLine1="        mAmInternal.enforceCallingPermission(READ_FRAME_BUFFER, &quot;getTaskSnapshot()&quot;);"
+        errorLine2="        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="out/soong/.intermediates/frameworks/base/services/core/services.core.protologsrc/gen/services.core.protolog.srcjar!/frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java"
+            line="3978"
+            column="9"/>
+    </issue>
+
+    <issue
+        id="SimpleManualPermissionEnforcement"
+        message="IActivityTaskManager permission check can be converted to @EnforcePermission annotation"
+        errorLine1="        mAmInternal.enforceCallingPermission(READ_FRAME_BUFFER, &quot;takeTaskSnapshot()&quot;);"
+        errorLine2="        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="out/soong/.intermediates/frameworks/base/services/core/services.core.protologsrc/gen/services.core.protolog.srcjar!/frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java"
+            line="4000"
+            column="9"/>
+    </issue>
+
+    <issue
+        id="SimpleManualPermissionEnforcement"
+        message="IActivityTaskManager permission check can be converted to @EnforcePermission annotation"
+        errorLine1="        mAmInternal.enforceCallingPermission("
+        errorLine2="        ^">
+        <location
+            file="out/soong/.intermediates/frameworks/base/services/core/services.core.protologsrc/gen/services.core.protolog.srcjar!/frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java"
+            line="4029"
+            column="9"/>
+    </issue>
+
+    <issue
+        id="SimpleManualPermissionEnforcement"
+        message="IActivityTaskManager permission check can be converted to @EnforcePermission annotation"
+        errorLine1="        mAmInternal.enforceCallingPermission(CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS,"
+        errorLine2="        ^">
+        <location
+            file="out/soong/.intermediates/frameworks/base/services/core/services.core.protologsrc/gen/services.core.protolog.srcjar!/frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java"
+            line="4057"
+            column="9"/>
+    </issue>
+
+    <issue
+        id="SimpleManualPermissionEnforcement"
+        message="IActivityTaskManager permission check can be converted to @EnforcePermission annotation"
+        errorLine1="        mAmInternal.enforceCallingPermission(CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS,"
+        errorLine2="        ^">
+        <location
+            file="out/soong/.intermediates/frameworks/base/services/core/services.core.protologsrc/gen/services.core.protolog.srcjar!/frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java"
+            line="4074"
+            column="9"/>
+    </issue>
+
+    <issue
+        id="SimpleManualPermissionEnforcement"
+        message="IActivityTaskManager permission check can be converted to @EnforcePermission annotation"
+        errorLine1="        mAmInternal.enforceCallingPermission(STOP_APP_SWITCHES, &quot;stopAppSwitches&quot;);"
+        errorLine2="        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="out/soong/.intermediates/frameworks/base/services/core/services.core.protologsrc/gen/services.core.protolog.srcjar!/frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java"
+            line="4136"
+            column="9"/>
+    </issue>
+
+    <issue
+        id="SimpleManualPermissionEnforcement"
+        message="IActivityTaskManager permission check can be converted to @EnforcePermission annotation"
+        errorLine1="        mAmInternal.enforceCallingPermission(STOP_APP_SWITCHES, &quot;resumeAppSwitches&quot;);"
+        errorLine2="        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="out/soong/.intermediates/frameworks/base/services/core/services.core.protologsrc/gen/services.core.protolog.srcjar!/frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java"
+            line="4147"
+            column="9"/>
+    </issue>
+
+    <issue
+        id="SimpleManualPermissionEnforcement"
+        message="IActivityTaskManager permission check can be converted to @EnforcePermission annotation"
+        errorLine1="        mAmInternal.enforceCallingPermission(android.Manifest.permission.SET_SCREEN_COMPATIBILITY,"
+        errorLine2="        ^">
+        <location
+            file="out/soong/.intermediates/frameworks/base/services/core/services.core.protologsrc/gen/services.core.protolog.srcjar!/frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java"
+            line="4198"
+            column="9"/>
+    </issue>
+
+    <issue
+        id="SimpleManualPermissionEnforcement"
+        message="IActivityTaskManager permission check can be converted to @EnforcePermission annotation"
+        errorLine1="        mAmInternal.enforceCallingPermission(android.Manifest.permission.SET_SCREEN_COMPATIBILITY,"
+        errorLine2="        ^">
+        <location
+            file="out/soong/.intermediates/frameworks/base/services/core/services.core.protologsrc/gen/services.core.protolog.srcjar!/frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java"
+            line="4215"
+            column="9"/>
+    </issue>
+
+    <issue
+        id="SimpleManualPermissionEnforcement"
+        message="IActivityTaskManager permission check can be converted to @EnforcePermission annotation"
+        errorLine1="        mAmInternal.enforceCallingPermission(CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS,"
+        errorLine2="        ^">
+        <location
+            file="out/soong/.intermediates/frameworks/base/services/core/services.core.protologsrc/gen/services.core.protolog.srcjar!/frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java"
+            line="4280"
+            column="9"/>
+    </issue>
+
+    <issue
+        id="SimpleManualPermissionEnforcement"
+        message="IActivityTaskManager permission check can be converted to @EnforcePermission annotation"
+        errorLine1="        mAmInternal.enforceCallingPermission(DETECT_SCREEN_CAPTURE,"
+        errorLine2="        ^">
+        <location
+            file="out/soong/.intermediates/frameworks/base/services/core/services.core.protologsrc/gen/services.core.protolog.srcjar!/frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java"
+            line="5836"
+            column="9"/>
+    </issue>
+
+    <issue
+        id="SimpleManualPermissionEnforcement"
+        message="IActivityTaskManager permission check can be converted to @EnforcePermission annotation"
+        errorLine1="        mAmInternal.enforceCallingPermission(DETECT_SCREEN_CAPTURE,"
+        errorLine2="        ^">
+        <location
+            file="out/soong/.intermediates/frameworks/base/services/core/services.core.protologsrc/gen/services.core.protolog.srcjar!/frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java"
+            line="5849"
+            column="9"/>
+    </issue>
+
+    <issue
+        id="SimpleManualPermissionEnforcement"
+        message="IColorDisplayManager permission check should be converted to @EnforcePermission annotation"
+        errorLine1="            getContext().enforceCallingOrSelfPermission("
+        errorLine2="            ^">
+        <location
+            file="frameworks/base/services/core/java/com/android/server/display/color/ColorDisplayService.java"
+            line="2152"
+            column="13"/>
+    </issue>
+
+    <issue
+        id="SimpleManualPermissionEnforcement"
+        message="IMediaProjectionManager permission check can be converted to @EnforcePermission annotation"
+        errorLine1="            if (mContext.checkCallingPermission(MANAGE_MEDIA_PROJECTION)"
+        errorLine2="            ^">
+        <location
+            file="frameworks/base/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java"
+            line="779"
+            column="13"/>
+    </issue>
+
+    <issue
+        id="SimpleManualPermissionEnforcement"
+        message="IMediaProjectionManager permission check can be converted to @EnforcePermission annotation"
+        errorLine1="            if (mContext.checkCallingPermission(MANAGE_MEDIA_PROJECTION)"
+        errorLine2="            ^">
+        <location
+            file="frameworks/base/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java"
+            line="820"
+            column="13"/>
+    </issue>
+
+    <issue
+        id="SimpleManualPermissionEnforcement"
+        message="IMediaProjectionManager permission check can be converted to @EnforcePermission annotation"
+        errorLine1="            if (mContext.checkCallingPermission(MANAGE_MEDIA_PROJECTION)"
+        errorLine2="            ^">
+        <location
+            file="frameworks/base/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java"
+            line="906"
+            column="13"/>
+    </issue>
+
+    <issue
+        id="SimpleManualPermissionEnforcement"
+        message="INotificationManager permission check can be converted to @EnforcePermission annotation"
+        errorLine1="            if (PERMISSION_GRANTED"
+        errorLine2="            ^">
+        <location
+            file="frameworks/base/services/core/java/com/android/server/notification/NotificationManagerService.java"
+            line="6691"
+            column="13"/>
+    </issue>
+
+    <issue
+        id="SimpleManualPermissionEnforcement"
+        message="INotificationManager permission check can be converted to @EnforcePermission annotation"
+        errorLine1="            if (PERMISSION_GRANTED"
+        errorLine2="            ^">
+        <location
+            file="frameworks/base/services/core/java/com/android/server/notification/NotificationManagerService.java"
+            line="6712"
+            column="13"/>
+    </issue>
+
+    <issue
+        id="SimpleManualPermissionEnforcement"
+        message="IOnDeviceIntelligenceManager permission check can be converted to @EnforcePermission annotation"
+        errorLine1="                mContext.enforceCallingPermission("
+        errorLine2="                ^">
+        <location
+            file="frameworks/base/services/core/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerService.java"
+            line="237"
+            column="17"/>
+    </issue>
+
+    <issue
+        id="SimpleManualPermissionEnforcement"
+        message="IPackageInstaller permission check should be converted to @EnforcePermission annotation"
+        errorLine1="        if (mContext.checkCallingOrSelfPermission(Manifest.permission.VERIFICATION_AGENT)"
+        errorLine2="        ^">
+        <location
+            file="frameworks/base/services/core/java/com/android/server/pm/PackageInstallerService.java"
+            line="1881"
+            column="9"/>
+    </issue>
+
+    <issue
+        id="SimpleManualPermissionEnforcement"
+        message="IPackageInstaller permission check should be converted to @EnforcePermission annotation"
+        errorLine1="        if (mContext.checkCallingOrSelfPermission(Manifest.permission.VERIFICATION_AGENT)"
+        errorLine2="        ^">
+        <location
+            file="frameworks/base/services/core/java/com/android/server/pm/PackageInstallerService.java"
+            line="1892"
+            column="9"/>
+    </issue>
+
+    <issue
+        id="SimpleManualPermissionEnforcement"
+        message="IPackageManager permission check can be converted to @EnforcePermission annotation"
+        errorLine1="            mContext.enforceCallingPermission(Manifest.permission.SEND_DEVICE_CUSTOMIZATION_READY,"
+        errorLine2="            ^">
+        <location
+            file="frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java"
+            line="5798"
+            column="13"/>
+    </issue>
+
+    <issue
+        id="SimpleManualPermissionEnforcement"
+        message="IPackageManager permission check can be converted to @EnforcePermission annotation"
+        errorLine1="            mContext.enforceCallingPermission("
+        errorLine2="            ^">
+        <location
+            file="frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java"
+            line="6266"
+            column="13"/>
+    </issue>
+
+    <issue
+        id="SimpleManualPermissionEnforcement"
+        message="ITvInputManager permission check can be converted to @EnforcePermission annotation"
+        errorLine1="            if (mContext.checkCallingPermission("
+        errorLine2="            ^">
+        <location
+            file="frameworks/base/services/core/java/com/android/server/tv/TvInputManagerService.java"
+            line="1406"
+            column="13"/>
+    </issue>
+
+    <issue
+        id="SimpleManualPermissionEnforcement"
+        message="ITvInputManager permission check can be converted to @EnforcePermission annotation"
+        errorLine1="            if (mContext.checkCallingPermission(android.Manifest.permission.NOTIFY_TV_INPUTS)"
+        errorLine2="            ^">
+        <location
+            file="frameworks/base/services/core/java/com/android/server/tv/TvInputManagerService.java"
+            line="1427"
+            column="13"/>
+    </issue>
+
+    <issue
+        id="SimpleManualPermissionEnforcement"
+        message="ITvInputManager permission check can be converted to @EnforcePermission annotation"
+        errorLine1="            if (mContext.checkCallingPermission("
+        errorLine2="            ^">
+        <location
+            file="frameworks/base/services/core/java/com/android/server/tv/TvInputManagerService.java"
+            line="1734"
+            column="13"/>
+    </issue>
+
+    <issue
+        id="SimpleManualPermissionEnforcement"
+        message="ITvInputManager permission check can be converted to @EnforcePermission annotation"
+        errorLine1="            if (mContext.checkCallingPermission(android.Manifest.permission.DVB_DEVICE)"
+        errorLine2="            ^">
+        <location
+            file="frameworks/base/services/core/java/com/android/server/tv/TvInputManagerService.java"
+            line="2509"
+            column="13"/>
+    </issue>
+
+    <issue
+        id="SimpleManualPermissionEnforcement"
+        message="ITvInputManager permission check can be converted to @EnforcePermission annotation"
+        errorLine1="            if (mContext.checkCallingPermission(android.Manifest.permission.DVB_DEVICE)"
+        errorLine2="            ^">
+        <location
+            file="frameworks/base/services/core/java/com/android/server/tv/TvInputManagerService.java"
+            line="2564"
+            column="13"/>
+    </issue>
+
+    <issue
+        id="SimpleManualPermissionEnforcement"
+        message="ITvInputManager permission check can be converted to @EnforcePermission annotation"
+        errorLine1="            if (mContext.checkCallingPermission(android.Manifest.permission.ACCESS_TUNED_INFO)"
+        errorLine2="            ^">
+        <location
+            file="frameworks/base/services/core/java/com/android/server/tv/TvInputManagerService.java"
+            line="2932"
+            column="13"/>
+    </issue>
+
+    <issue
+        id="SimpleManualPermissionEnforcement"
+        message="IUriGrantsManager permission check can be converted to @EnforcePermission annotation"
+        errorLine1="        mAmInternal.enforceCallingPermission("
+        errorLine2="        ^">
+        <location
+            file="frameworks/base/services/core/java/com/android/server/uri/UriGrantsManagerService.java"
+            line="366"
+            column="9"/>
+    </issue>
+
+    <issue
+        id="SimpleManualPermissionEnforcement"
+        message="IUriGrantsManager permission check can be converted to @EnforcePermission annotation"
+        errorLine1="        mAmInternal.enforceCallingPermission("
+        errorLine2="        ^">
+        <location
+            file="frameworks/base/services/core/java/com/android/server/uri/UriGrantsManagerService.java"
+            line="444"
+            column="9"/>
+    </issue>
+
+    <issue
+        id="SimpleManualPermissionEnforcement"
+        message="IVcnManagementService permission check should be converted to @EnforcePermission annotation"
+        errorLine1="        mContext.enforceCallingOrSelfPermission(DUMP, TAG);"
+        errorLine2="        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="frameworks/base/services/core/java/com/android/server/VcnManagementService.java"
+            line="1329"
+            column="9"/>
+    </issue>
+
+</issues>
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 6baab25..3805c02 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -28,6 +28,7 @@
 import static android.view.Display.DEFAULT_DISPLAY;
 
 import static com.android.server.utils.TimingsTraceAndSlog.SYSTEM_SERVER_TIMING_TAG;
+import static com.android.tradeinmode.flags.Flags.enableTradeInMode;
 
 import android.annotation.NonNull;
 import android.annotation.StringRes;
@@ -190,6 +191,7 @@
 import com.android.server.media.MediaSessionService;
 import com.android.server.media.metrics.MediaMetricsManagerService;
 import com.android.server.media.projection.MediaProjectionManagerService;
+import com.android.server.media.quality.MediaQualityService;
 import com.android.server.midi.MidiService;
 import com.android.server.musicrecognition.MusicRecognitionManagerService;
 import com.android.server.net.NetworkManagementService;
@@ -1399,10 +1401,6 @@
         mSystemServiceManager.startService(BatteryService.class);
         t.traceEnd();
 
-        t.traceBegin("StartTradeInModeService");
-        mSystemServiceManager.startService(TradeInModeService.class);
-        t.traceEnd();
-
         // Tracks application usage stats.
         t.traceBegin("StartUsageService");
         mSystemServiceManager.startService(UsageStatsService.class);
@@ -1772,6 +1770,13 @@
                 mSystemServiceManager.startService(AdvancedProtectionService.Lifecycle.class);
                 t.traceEnd();
             }
+
+            if (!isWatch && !isTv && !isAutomotive && enableTradeInMode()) {
+                t.traceBegin("StartTradeInModeService");
+                mSystemServiceManager.startService(TradeInModeService.class);
+                t.traceEnd();
+            }
+
         } catch (Throwable e) {
             Slog.e("System", "******************************************");
             Slog.e("System", "************ Failure starting core service");
@@ -2583,6 +2588,10 @@
                 t.traceEnd();
             }
 
+            t.traceBegin("StartMediaQuality");
+            mSystemServiceManager.startService(MediaQualityService.class);
+            t.traceEnd();
+
             if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_PICTURE_IN_PICTURE)) {
                 t.traceBegin("StartMediaResourceMonitor");
                 mSystemServiceManager.startService(MediaResourceMonitorService.class);
diff --git a/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java b/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
index 105147f..4e86888 100644
--- a/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
+++ b/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
@@ -16,6 +16,8 @@
 
 package com.android.server.profcollect;
 
+import android.Manifest;
+import android.annotation.RequiresPermission;
 import android.app.job.JobInfo;
 import android.app.job.JobParameters;
 import android.app.job.JobScheduler;
@@ -26,6 +28,7 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.hardware.camera2.CameraManager;
+import android.hardware.usb.UsbManager;
 import android.os.Handler;
 import android.os.IBinder.DeathRecipient;
 import android.os.Looper;
@@ -67,6 +70,8 @@
     private int mUsageSetting;
     private boolean mUploadEnabled;
 
+    private boolean mAdbActive;
+
     private IProfCollectd mIProfcollect;
     private static ProfcollectForwardingService sSelfService;
     private final Handler mHandler = new ProfcollectdHandler(IoThread.getHandler().getLooper());
@@ -84,6 +89,15 @@
                 Log.d(LOG_TAG, "Received broadcast to pack and upload reports");
                 createAndUploadReport(sSelfService);
             }
+            if (UsbManager.ACTION_USB_STATE.equals(intent.getAction())) {
+                boolean isADB = intent.getBooleanExtra(UsbManager.USB_FUNCTION_ADB, false);
+                if (isADB) {
+                    boolean connected = intent.getBooleanExtra(UsbManager.USB_CONNECTED, false);
+                    Log.d(LOG_TAG, "Received broadcast that ADB became " + connected
+                            + ", was " + mAdbActive);
+                    mAdbActive = connected;
+                }
+            }
         }
     };
 
@@ -108,6 +122,7 @@
 
         final IntentFilter filter = new IntentFilter();
         filter.addAction(INTENT_UPLOAD_PROFILES);
+        filter.addAction(UsbManager.ACTION_USB_STATE);
         context.registerReceiver(mBroadcastReceiver, filter, Context.RECEIVER_NOT_EXPORTED);
     }
 
@@ -125,7 +140,13 @@
     }
 
     @Override
+    @RequiresPermission(Manifest.permission.MANAGE_USB)
     public void onBootPhase(int phase) {
+        if (phase == PHASE_SYSTEM_SERVICES_READY) {
+            UsbManager usbManager = getContext().getSystemService(UsbManager.class);
+            mAdbActive = ((usbManager.getCurrentFunctions() & UsbManager.FUNCTION_ADB) == 1);
+            Log.d(LOG_TAG, "ADB is " + mAdbActive + " on system startup");
+        }
         if (phase == PHASE_BOOT_COMPLETED) {
             if (mIProfcollect == null) {
                 return;
@@ -281,6 +302,9 @@
             if (mIProfcollect == null) {
                 return;
             }
+            if (mAdbActive) {
+                return;
+            }
             if (Utils.withFrequency("applaunch_trace_freq", 5)) {
                 Utils.traceSystem(mIProfcollect, "applaunch");
             }
@@ -303,6 +327,9 @@
         if (mIProfcollect == null) {
             return;
         }
+        if (mAdbActive) {
+            return;
+        }
         if (Utils.withFrequency("dex2oat_trace_freq", 25)) {
             // Dex2oat could take a while before it starts. Add a short delay before start tracing.
             Utils.traceSystem(mIProfcollect, "dex2oat", /* delayMs */ 1000);
diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/InputMethodServiceTest.java b/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/InputMethodServiceTest.java
index 2bc8af1..5bb6b19 100644
--- a/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/InputMethodServiceTest.java
+++ b/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/InputMethodServiceTest.java
@@ -85,6 +85,8 @@
     private static final String DISABLE_SHOW_IME_WITH_HARD_KEYBOARD_CMD =
             "settings put secure " + Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD + " 0";
 
+    private final DeviceFlagsValueProvider mFlagsValueProvider = new DeviceFlagsValueProvider();
+
     private Instrumentation mInstrumentation;
     private UiDevice mUiDevice;
     private Context mContext;
@@ -95,7 +97,7 @@
     private boolean mShowImeWithHardKeyboardEnabled;
 
     @Rule
-    public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+    public final CheckFlagsRule mCheckFlagsRule = new CheckFlagsRule(mFlagsValueProvider);
 
     @Before
     public void setUp() throws Exception {
@@ -159,7 +161,7 @@
 
         // Press home key to hide soft keyboard.
         Log.i(TAG, "Press home");
-        if (Flags.refactorInsetsController()) {
+        if (mFlagsValueProvider.getBoolean(Flags.FLAG_REFACTOR_INSETS_CONTROLLER)) {
             assertThat(mUiDevice.pressHome()).isTrue();
             // The IME visibility is only sent at the end of the animation. Therefore, we have to
             // wait until the visibility was sent to the server and the IME window hidden.
@@ -774,7 +776,7 @@
         backButtonUiObject.click();
         mInstrumentation.waitForIdleSync();
 
-        if (Flags.refactorInsetsController()) {
+        if (mFlagsValueProvider.getBoolean(Flags.FLAG_REFACTOR_INSETS_CONTROLLER)) {
             // The IME visibility is only sent at the end of the animation. Therefore, we have to
             // wait until the visibility was sent to the server and the IME window hidden.
             eventually(() -> assertThat(mInputMethodService.isInputViewShown()).isFalse());
@@ -812,7 +814,7 @@
         backButtonUiObject.longClick();
         mInstrumentation.waitForIdleSync();
 
-        if (Flags.refactorInsetsController()) {
+        if (mFlagsValueProvider.getBoolean(Flags.FLAG_REFACTOR_INSETS_CONTROLLER)) {
             // The IME visibility is only sent at the end of the animation. Therefore, we have to
             // wait until the visibility was sent to the server and the IME window hidden.
             eventually(() -> assertThat(mInputMethodService.isInputViewShown()).isFalse());
@@ -900,7 +902,7 @@
         assertWithMessage("Input Method Switcher Menu is shown")
                 .that(isInputMethodPickerShown(imm))
                 .isTrue();
-        if (Flags.refactorInsetsController()) {
+        if (mFlagsValueProvider.getBoolean(Flags.FLAG_REFACTOR_INSETS_CONTROLLER)) {
             // The IME visibility is only sent at the end of the animation. Therefore, we have to
             // wait until the visibility was sent to the server and the IME window hidden.
             eventually(() -> assertThat(mInputMethodService.isInputViewShown()).isFalse());
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 6afcae7..3aeab09 100644
--- a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java
+++ b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java
@@ -76,8 +76,10 @@
 @RunWith(AndroidJUnit4.class)
 public class DefaultImeVisibilityApplierTest extends InputMethodManagerServiceTestBase {
 
+    private final DeviceFlagsValueProvider mFlagsValueProvider = new DeviceFlagsValueProvider();
+
     @Rule
-    public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+    public final CheckFlagsRule mCheckFlagsRule = new CheckFlagsRule(mFlagsValueProvider);
     private DefaultImeVisibilityApplier mVisibilityApplier;
 
     @Before
@@ -151,7 +153,7 @@
             mVisibilityApplier.applyImeVisibility(mWindowToken, ImeTracker.Token.empty(),
                     STATE_HIDE_IME_EXPLICIT, eq(SoftInputShowHideReason.NOT_SET), mUserId);
         }
-        if (Flags.refactorInsetsController()) {
+        if (mFlagsValueProvider.getBoolean(Flags.FLAG_REFACTOR_INSETS_CONTROLLER)) {
             verifySetImeVisibility(true /* setVisible */, false /* invoked */);
             verifySetImeVisibility(false /* setVisible */, true /* invoked */);
         } else {
@@ -168,7 +170,7 @@
             mVisibilityApplier.applyImeVisibility(mWindowToken, ImeTracker.Token.empty(),
                     STATE_HIDE_IME_NOT_ALWAYS, eq(SoftInputShowHideReason.NOT_SET), mUserId);
         }
-        if (Flags.refactorInsetsController()) {
+        if (mFlagsValueProvider.getBoolean(Flags.FLAG_REFACTOR_INSETS_CONTROLLER)) {
             verifySetImeVisibility(true /* setVisible */, false /* invoked */);
             verifySetImeVisibility(false /* setVisible */, true /* invoked */);
         } else {
@@ -182,7 +184,7 @@
             mVisibilityApplier.applyImeVisibility(mWindowToken, ImeTracker.Token.empty(),
                     STATE_SHOW_IME_IMPLICIT, eq(SoftInputShowHideReason.NOT_SET), mUserId);
         }
-        if (Flags.refactorInsetsController()) {
+        if (mFlagsValueProvider.getBoolean(Flags.FLAG_REFACTOR_INSETS_CONTROLLER)) {
             verifySetImeVisibility(true /* setVisible */, true /* invoked */);
             verifySetImeVisibility(false /* setVisible */, false /* invoked */);
         } else {
@@ -260,7 +262,7 @@
             verify(mVisibilityApplier).applyImeVisibility(
                     eq(mWindowToken), any(), eq(STATE_HIDE_IME),
                     eq(SoftInputShowHideReason.NOT_SET), eq(mUserId) /* userId */);
-            if (!Flags.refactorInsetsController()) {
+            if (!mFlagsValueProvider.getBoolean(Flags.FLAG_REFACTOR_INSETS_CONTROLLER)) {
                 verify(mInputMethodManagerService.mWindowManagerInternal).hideIme(eq(mWindowToken),
                         eq(displayIdToShowIme), and(not(eq(statsToken)), notNull()));
             }
diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceWindowGainedFocusTest.java b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceWindowGainedFocusTest.java
index 4d956b2..c958bd3 100644
--- a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceWindowGainedFocusTest.java
+++ b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceWindowGainedFocusTest.java
@@ -39,8 +39,10 @@
 import android.os.IBinder;
 import android.os.LocaleList;
 import android.os.RemoteException;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
 import android.util.Log;
 import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.Flags;
 import android.window.ImeOnBackInvokedDispatcher;
 
 import com.android.internal.inputmethod.IInputMethodClient;
@@ -89,6 +91,9 @@
             };
     private static final int DEFAULT_SOFT_INPUT_FLAG =
             StartInputFlags.VIEW_HAS_FOCUS | StartInputFlags.IS_TEXT_EDITOR;
+
+    private final DeviceFlagsValueProvider mFlagsValueProvider = new DeviceFlagsValueProvider();
+
     @Mock
     VirtualDeviceManagerInternal mMockVdmInternal;
 
@@ -125,7 +130,7 @@
             case SOFT_INPUT_STATE_UNSPECIFIED:
                 boolean showSoftInput =
                         (mSoftInputAdjustment == SOFT_INPUT_ADJUST_RESIZE) || mIsLargeScreen;
-                if (android.view.inputmethod.Flags.refactorInsetsController()) {
+                if (mFlagsValueProvider.getBoolean(Flags.FLAG_REFACTOR_INSETS_CONTROLLER)) {
                     verifySetImeVisibility(true /* setVisible */, showSoftInput /* invoked */);
                     // A hide can only be triggered if there is no editorFocused, which this test
                     // always sets.
@@ -141,7 +146,7 @@
                 break;
             case SOFT_INPUT_STATE_VISIBLE:
             case SOFT_INPUT_STATE_ALWAYS_VISIBLE:
-                if (android.view.inputmethod.Flags.refactorInsetsController()) {
+                if (mFlagsValueProvider.getBoolean(Flags.FLAG_REFACTOR_INSETS_CONTROLLER)) {
                     verifySetImeVisibility(true /* setVisible */, true /* invoked */);
                     verifySetImeVisibility(false /* setVisible */, false /* invoked */);
                 } else {
@@ -150,7 +155,7 @@
                 }
                 break;
             case SOFT_INPUT_STATE_UNCHANGED:
-                if (android.view.inputmethod.Flags.refactorInsetsController()) {
+                if (mFlagsValueProvider.getBoolean(Flags.FLAG_REFACTOR_INSETS_CONTROLLER)) {
                     verifySetImeVisibility(true /* setVisible */, false /* invoked */);
                     verifySetImeVisibility(false /* setVisible */, false /* invoked */);
                 } else {
@@ -160,7 +165,7 @@
                 break;
             case SOFT_INPUT_STATE_HIDDEN:
             case SOFT_INPUT_STATE_ALWAYS_HIDDEN:
-                if (android.view.inputmethod.Flags.refactorInsetsController()) {
+                if (mFlagsValueProvider.getBoolean(Flags.FLAG_REFACTOR_INSETS_CONTROLLER)) {
                     verifySetImeVisibility(true /* setVisible */, false /* invoked */);
                     // In this case, we don't have to manipulate the requested visible types of
                     // the WindowState, as they're already in the correct state
@@ -192,7 +197,7 @@
             case SOFT_INPUT_STATE_UNSPECIFIED:
                 boolean hideSoftInput =
                         (mSoftInputAdjustment != SOFT_INPUT_ADJUST_RESIZE) && !mIsLargeScreen;
-                if (android.view.inputmethod.Flags.refactorInsetsController()) {
+                if (mFlagsValueProvider.getBoolean(Flags.FLAG_REFACTOR_INSETS_CONTROLLER)) {
                     // A show can only be triggered in forward navigation
                     verifySetImeVisibility(false /* setVisible */, false /* invoked */);
                     // A hide can only be triggered if there is no editorFocused, which this test
@@ -209,7 +214,7 @@
             case SOFT_INPUT_STATE_VISIBLE:
             case SOFT_INPUT_STATE_HIDDEN:
             case SOFT_INPUT_STATE_UNCHANGED: // Do nothing
-                if (android.view.inputmethod.Flags.refactorInsetsController()) {
+                if (mFlagsValueProvider.getBoolean(Flags.FLAG_REFACTOR_INSETS_CONTROLLER)) {
                     verifySetImeVisibility(true /* setVisible */, false /* invoked */);
                     verifySetImeVisibility(false /* setVisible */, false /* invoked */);
                 } else {
@@ -218,7 +223,7 @@
                 }
                 break;
             case SOFT_INPUT_STATE_ALWAYS_VISIBLE:
-                if (android.view.inputmethod.Flags.refactorInsetsController()) {
+                if (mFlagsValueProvider.getBoolean(Flags.FLAG_REFACTOR_INSETS_CONTROLLER)) {
                     verifySetImeVisibility(true /* setVisible */, true /* invoked */);
                     verifySetImeVisibility(false /* setVisible */, false /* invoked */);
                 } else {
@@ -227,7 +232,7 @@
                 }
                 break;
             case SOFT_INPUT_STATE_ALWAYS_HIDDEN:
-                if (android.view.inputmethod.Flags.refactorInsetsController()) {
+                if (mFlagsValueProvider.getBoolean(Flags.FLAG_REFACTOR_INSETS_CONTROLLER)) {
                     verifySetImeVisibility(true /* setVisible */, false /* invoked */);
                     // In this case, we don't have to manipulate the requested visible types of
                     // the WindowState, as they're already in the correct state
diff --git a/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt b/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt
index 5c4716d..7d5532f 100644
--- a/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt
+++ b/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt
@@ -57,6 +57,7 @@
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.Parameterized
+import org.mockito.ArgumentMatchers.eq
 import org.mockito.Mockito.any
 import org.mockito.Mockito.anyInt
 import org.mockito.Mockito.doReturn
@@ -383,6 +384,10 @@
                     android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)) {
                 PackageManager.PERMISSION_GRANTED
             }
+            whenever(this.checkPermission(
+                eq(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL), anyInt(), anyInt())) {
+                PackageManager.PERMISSION_GRANTED
+            }
         }
         val mockSharedLibrariesImpl: SharedLibrariesImpl = mock {
             whenever(this.snapshot()) { this@mock }
diff --git a/services/tests/PackageManagerServiceTests/appenumeration/src/com/android/server/pm/test/appenumeration/AppEnumerationInternalTests.java b/services/tests/PackageManagerServiceTests/appenumeration/src/com/android/server/pm/test/appenumeration/AppEnumerationInternalTests.java
index 4012d8e..9f02b3f 100644
--- a/services/tests/PackageManagerServiceTests/appenumeration/src/com/android/server/pm/test/appenumeration/AppEnumerationInternalTests.java
+++ b/services/tests/PackageManagerServiceTests/appenumeration/src/com/android/server/pm/test/appenumeration/AppEnumerationInternalTests.java
@@ -33,6 +33,7 @@
 import android.os.Process;
 import android.os.ServiceManager;
 import android.os.UserHandle;
+import android.view.Display;
 
 import androidx.test.platform.app.InstrumentationRegistry;
 import androidx.test.runner.AndroidJUnit4;
@@ -174,7 +175,8 @@
                         ServiceManager.getService(MEDIA_PROJECTION_SERVICE));
 
         assertThat(mediaProjectionManager.createProjection(0 /* uid */, TARGET_SHARED_USER,
-                MediaProjectionManager.TYPE_SCREEN_CAPTURE, false /* permanentGrant */))
+                MediaProjectionManager.TYPE_SCREEN_CAPTURE, false /* permanentGrant */,
+                Display.DEFAULT_DISPLAY /* displayId */))
                 .isNotNull();
     }
 
@@ -187,7 +189,8 @@
 
         Assert.assertThrows(IllegalArgumentException.class,
                 () -> mediaProjectionManager.createProjection(0 /* uid */, TARGET_SHARED_USER,
-                        MediaProjectionManager.TYPE_SCREEN_CAPTURE, false /* permanentGrant */));
+                        MediaProjectionManager.TYPE_SCREEN_CAPTURE, false /* permanentGrant */,
+                        Display.DEFAULT_DISPLAY /* displayId */));
     }
 
     private static void installPackage(String apkPath, boolean forceQueryable) {
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageInstallerSessionTest.kt b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageInstallerSessionTest.kt
index 1e89359..09d0e4a 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageInstallerSessionTest.kt
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageInstallerSessionTest.kt
@@ -22,6 +22,7 @@
 import android.content.pm.PackageInstaller.SessionParams.PERMISSION_STATE_DENIED
 import android.content.pm.PackageInstaller.SessionParams.PERMISSION_STATE_GRANTED
 import android.content.pm.PackageInstaller.VERIFICATION_POLICY_BLOCK_FAIL_CLOSED
+import android.content.pm.PackageInstaller.VERIFICATION_POLICY_BLOCK_FAIL_OPEN
 import android.content.pm.PackageManager
 import android.content.pm.verify.domain.DomainSet
 import android.os.Parcel
@@ -199,7 +200,8 @@
             /* stagedSessionErrorMessage */ "some error",
             /* preVerifiedDomains */ DomainSet(setOf("com.foo", "com.bar")),
             /* VerifierController */ mock(VerifierController::class.java),
-            VERIFICATION_POLICY_BLOCK_FAIL_CLOSED
+            /* initialVerificationPolicy */ VERIFICATION_POLICY_BLOCK_FAIL_OPEN,
+            /* currentVerificationPolicy */ VERIFICATION_POLICY_BLOCK_FAIL_CLOSED
         )
     }
 
@@ -342,7 +344,8 @@
         assertThat(expected.childSessionIds).asList()
             .containsExactlyElementsIn(actual.childSessionIds.toList())
         assertThat(expected.preVerifiedDomains).isEqualTo(actual.preVerifiedDomains)
-        assertThat(expected.verificationPolicy).isEqualTo(actual.verificationPolicy)
+        assertThat(expected.initialVerificationPolicy).isEqualTo(actual.initialVerificationPolicy)
+        assertThat(expected.currentVerificationPolicy).isEqualTo(actual.currentVerificationPolicy)
     }
 
     private fun assertInstallSourcesEquivalent(expected: InstallSource, actual: InstallSource) {
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/SystemPartitionParseTest.kt b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/SystemPartitionParseTest.kt
index 1d668cd..13cf125 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/SystemPartitionParseTest.kt
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/SystemPartitionParseTest.kt
@@ -17,6 +17,7 @@
 package com.android.server.pm.parsing
 
 import android.content.pm.PackageManager
+import android.content.pm.parsing.ApkLiteParseUtils
 import android.platform.test.annotations.Postsubmit
 import com.android.internal.pm.parsing.PackageParserException
 import com.android.internal.pm.pkg.parsing.ParsingPackageUtils
@@ -81,8 +82,10 @@
         val exceptions = buildApks()
                 .map {
                     runCatching {
-                        parser.parsePackage(
+                        if (ApkLiteParseUtils.isApkFile(it) || it.isDirectory()) {
+                            parser.parsePackage(
                                 it, ParsingPackageUtils.PARSE_IS_SYSTEM_DIR, false /*useCaches*/)
+                        }
                     }
                 }
                 .mapNotNull { it.exceptionOrNull() }
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/verify/pkg/VerifierControllerTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/verify/pkg/VerifierControllerTest.java
index 2461798..3046d4b 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/verify/pkg/VerifierControllerTest.java
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/verify/pkg/VerifierControllerTest.java
@@ -24,6 +24,7 @@
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
@@ -49,6 +50,7 @@
 
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
 
 import com.android.internal.infra.AndroidFuture;
 import com.android.internal.infra.ServiceConnector;
@@ -122,6 +124,10 @@
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
+        // Mock that the UID of this test becomes the UID of the verifier
+        when(mSnapshot.getPackageUidInternal(anyString(), anyLong(), anyInt(), anyInt()))
+                .thenReturn(InstrumentationRegistry.getInstrumentation().getContext()
+                        .getApplicationInfo().uid);
         when(mInjector.getVerifierPackageName(any(Computer.class), anyInt())).thenReturn(
                 TEST_VERIFIER_COMPONENT_NAME.getPackageName());
         when(mInjector.getRemoteService(
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayTopologyCoordinatorTest.kt b/services/tests/displayservicetests/src/com/android/server/display/DisplayTopologyCoordinatorTest.kt
index 17af633..85e7356 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayTopologyCoordinatorTest.kt
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayTopologyCoordinatorTest.kt
@@ -21,7 +21,7 @@
 import android.view.DisplayInfo
 import org.junit.Before
 import org.junit.Test
-import org.mockito.ArgumentMatchers.anyDouble
+import org.mockito.ArgumentMatchers.anyFloat
 import org.mockito.ArgumentMatchers.anyInt
 import org.mockito.kotlin.mock
 import org.mockito.kotlin.never
@@ -55,9 +55,9 @@
 
         coordinator.onDisplayAdded(displayInfo)
 
-        val widthDp = displayInfo.logicalWidth * (DisplayMetrics.DENSITY_DEFAULT.toDouble()
+        val widthDp = displayInfo.logicalWidth * (DisplayMetrics.DENSITY_DEFAULT.toFloat()
                 / displayInfo.logicalDensityDpi)
-        val heightDp = displayInfo.logicalHeight * (DisplayMetrics.DENSITY_DEFAULT.toDouble()
+        val heightDp = displayInfo.logicalHeight * (DisplayMetrics.DENSITY_DEFAULT.toFloat()
                 / displayInfo.logicalDensityDpi)
         verify(mockTopology).addDisplay(displayInfo.displayId, widthDp, heightDp)
     }
@@ -68,7 +68,7 @@
 
         coordinator.onDisplayAdded(displayInfo)
 
-        verify(mockTopology, never()).addDisplay(anyInt(), anyDouble(), anyDouble())
+        verify(mockTopology, never()).addDisplay(anyInt(), anyFloat(), anyFloat())
     }
 
     @Test
@@ -78,6 +78,6 @@
 
         coordinator.onDisplayAdded(displayInfo)
 
-        verify(mockTopology, never()).addDisplay(anyInt(), anyDouble(), anyDouble())
+        verify(mockTopology, never()).addDisplay(anyInt(), anyFloat(), anyFloat())
     }
 }
\ No newline at end of file
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayTopologyTest.kt b/services/tests/displayservicetests/src/com/android/server/display/DisplayTopologyTest.kt
index f3a8d841..cd8c26d 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayTopologyTest.kt
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayTopologyTest.kt
@@ -17,6 +17,9 @@
 package com.android.server.display
 
 import android.view.Display
+import com.android.server.display.DisplayTopology.TreeNode.Position.POSITION_BOTTOM
+import com.android.server.display.DisplayTopology.TreeNode.Position.POSITION_TOP
+import com.android.server.display.DisplayTopology.TreeNode.Position.POSITION_RIGHT
 import com.google.common.truth.Truth.assertThat
 import org.junit.Test
 
@@ -26,8 +29,8 @@
     @Test
     fun addOneDisplay() {
         val displayId = 1
-        val width = 800.0
-        val height = 600.0
+        val width = 800f
+        val height = 600f
 
         topology.addDisplay(displayId, width, height)
 
@@ -43,12 +46,12 @@
     @Test
     fun addTwoDisplays() {
         val displayId1 = 1
-        val width1 = 800.0
-        val height1 = 600.0
+        val width1 = 800f
+        val height1 = 600f
 
         val displayId2 = 2
-        val width2 = 1000.0
-        val height2 = 1500.0
+        val width2 = 1000f
+        val height2 = 1500f
 
         topology.addDisplay(displayId1, width1, height1)
         topology.addDisplay(displayId2, width2, height2)
@@ -66,20 +69,19 @@
         assertThat(display2.mWidth).isEqualTo(width2)
         assertThat(display2.mHeight).isEqualTo(height2)
         assertThat(display2.mChildren).isEmpty()
-        assertThat(display2.mPosition).isEqualTo(
-            DisplayTopology.TreeNode.Position.POSITION_TOP)
+        assertThat(display2.mPosition).isEqualTo(POSITION_TOP)
         assertThat(display2.mOffset).isEqualTo(width1 / 2 - width2 / 2)
     }
 
     @Test
     fun addManyDisplays() {
         val displayId1 = 1
-        val width1 = 800.0
-        val height1 = 600.0
+        val width1 = 800f
+        val height1 = 600f
 
         val displayId2 = 2
-        val width2 = 1000.0
-        val height2 = 1500.0
+        val width2 = 1000f
+        val height2 = 1500f
 
         topology.addDisplay(displayId1, width1, height1)
         topology.addDisplay(displayId2, width2, height2)
@@ -102,8 +104,7 @@
         assertThat(display2.mWidth).isEqualTo(width2)
         assertThat(display2.mHeight).isEqualTo(height2)
         assertThat(display2.mChildren).hasSize(1)
-        assertThat(display2.mPosition).isEqualTo(
-            DisplayTopology.TreeNode.Position.POSITION_TOP)
+        assertThat(display2.mPosition).isEqualTo(POSITION_TOP)
         assertThat(display2.mOffset).isEqualTo(width1 / 2 - width2 / 2)
 
         var display = display2
@@ -114,8 +115,7 @@
             assertThat(display.mHeight).isEqualTo(height1)
             // The last display should have no children
             assertThat(display.mChildren).hasSize(if (i < noOfDisplays) 1 else 0)
-            assertThat(display.mPosition).isEqualTo(
-                DisplayTopology.TreeNode.Position.POSITION_RIGHT)
+            assertThat(display.mPosition).isEqualTo(POSITION_RIGHT)
             assertThat(display.mOffset).isEqualTo(0)
         }
     }
@@ -123,12 +123,12 @@
     @Test
     fun removeDisplays() {
         val displayId1 = 1
-        val width1 = 800.0
-        val height1 = 600.0
+        val width1 = 800f
+        val height1 = 600f
 
         val displayId2 = 2
-        val width2 = 1000.0
-        val height2 = 1500.0
+        val width2 = 1000f
+        val height2 = 1500f
 
         topology.addDisplay(displayId1, width1, height1)
         topology.addDisplay(displayId2, width2, height2)
@@ -154,8 +154,7 @@
         assertThat(display2.mWidth).isEqualTo(width2)
         assertThat(display2.mHeight).isEqualTo(height2)
         assertThat(display2.mChildren).hasSize(1)
-        assertThat(display2.mPosition).isEqualTo(
-            DisplayTopology.TreeNode.Position.POSITION_TOP)
+        assertThat(display2.mPosition).isEqualTo(POSITION_TOP)
         assertThat(display2.mOffset).isEqualTo(width1 / 2 - width2 / 2)
 
         var display = display2
@@ -169,8 +168,7 @@
             assertThat(display.mHeight).isEqualTo(height1)
             // The last display should have no children
             assertThat(display.mChildren).hasSize(if (i < noOfDisplays) 1 else 0)
-            assertThat(display.mPosition).isEqualTo(
-                DisplayTopology.TreeNode.Position.POSITION_RIGHT)
+            assertThat(display.mPosition).isEqualTo(POSITION_RIGHT)
             assertThat(display.mOffset).isEqualTo(0)
         }
 
@@ -194,8 +192,7 @@
         assertThat(display2.mWidth).isEqualTo(width2)
         assertThat(display2.mHeight).isEqualTo(height2)
         assertThat(display2.mChildren).hasSize(1)
-        assertThat(display2.mPosition).isEqualTo(
-            DisplayTopology.TreeNode.Position.POSITION_TOP)
+        assertThat(display2.mPosition).isEqualTo(POSITION_TOP)
         assertThat(display2.mOffset).isEqualTo(width1 / 2 - width2 / 2)
 
         display = display2
@@ -209,8 +206,7 @@
             assertThat(display.mHeight).isEqualTo(height1)
             // The last display should have no children
             assertThat(display.mChildren).hasSize(if (i < noOfDisplays) 1 else 0)
-            assertThat(display.mPosition).isEqualTo(
-                DisplayTopology.TreeNode.Position.POSITION_RIGHT)
+            assertThat(display.mPosition).isEqualTo(POSITION_RIGHT)
             assertThat(display.mOffset).isEqualTo(0)
         }
     }
@@ -218,8 +214,8 @@
     @Test
     fun removeAllDisplays() {
         val displayId = 1
-        val width = 800.0
-        val height = 600.0
+        val width = 800f
+        val height = 600f
 
         topology.addDisplay(displayId, width, height)
         topology.removeDisplay(displayId)
@@ -231,8 +227,8 @@
     @Test
     fun removeDisplayThatDoesNotExist() {
         val displayId = 1
-        val width = 800.0
-        val height = 600.0
+        val width = 800f
+        val height = 600f
 
         topology.addDisplay(displayId, width, height)
         topology.removeDisplay(3)
@@ -245,4 +241,236 @@
         assertThat(display.mHeight).isEqualTo(height)
         assertThat(display.mChildren).isEmpty()
     }
+
+    @Test
+    fun removePrimaryDisplay() {
+        val displayId1 = 1
+        val displayId2 = 2
+        val width = 800f
+        val height = 600f
+
+        topology.addDisplay(displayId1, width, height)
+        topology.addDisplay(displayId2, width, height)
+        topology.mPrimaryDisplayId = displayId2
+        topology.removeDisplay(displayId2)
+
+        assertThat(topology.mPrimaryDisplayId).isEqualTo(displayId1)
+        val display = topology.mRoot!!
+        assertThat(display.mDisplayId).isEqualTo(displayId1)
+        assertThat(display.mWidth).isEqualTo(width)
+        assertThat(display.mHeight).isEqualTo(height)
+        assertThat(display.mChildren).isEmpty()
+    }
+
+    @Test
+    fun normalization_noOverlaps_leavesTopologyUnchanged() {
+        val display1 = DisplayTopology.TreeNode(/* displayId= */ 1, /* width= */ 200f,
+            /* height= */ 600f, /* position= */ null, /* offset= */ 0f)
+        topology.mRoot = display1
+
+        val display2 = DisplayTopology.TreeNode(/* displayId= */ 2, /* width= */ 600f,
+            /* height= */ 200f, POSITION_RIGHT, /* offset= */ 0f)
+        display1.mChildren.add(display2)
+
+        val primaryDisplayId = 3
+        val display3 = DisplayTopology.TreeNode(primaryDisplayId, /* width= */ 600f,
+            /* height= */ 200f, POSITION_RIGHT, /* offset= */ 400f)
+        display1.mChildren.add(display3)
+        topology.mPrimaryDisplayId = primaryDisplayId
+
+        val display4 = DisplayTopology.TreeNode(/* displayId= */ 4, /* width= */ 200f,
+            /* height= */ 600f, POSITION_RIGHT, /* offset= */ 0f)
+        display2.mChildren.add(display4)
+
+        topology.normalize()
+
+        assertThat(topology.mPrimaryDisplayId).isEqualTo(primaryDisplayId)
+
+        val actualDisplay1 = topology.mRoot!!
+        assertThat(actualDisplay1.mDisplayId).isEqualTo(1)
+        assertThat(actualDisplay1.mWidth).isEqualTo(200f)
+        assertThat(actualDisplay1.mHeight).isEqualTo(600f)
+        assertThat(actualDisplay1.mChildren).hasSize(2)
+
+        val actualDisplay2 = actualDisplay1.mChildren[0]
+        assertThat(actualDisplay2.mDisplayId).isEqualTo(2)
+        assertThat(actualDisplay2.mWidth).isEqualTo(600f)
+        assertThat(actualDisplay2.mHeight).isEqualTo(200f)
+        assertThat(actualDisplay2.mPosition).isEqualTo(POSITION_RIGHT)
+        assertThat(actualDisplay2.mOffset).isEqualTo(0f)
+        assertThat(actualDisplay2.mChildren).hasSize(1)
+
+        val actualDisplay3 = actualDisplay1.mChildren[1]
+        assertThat(actualDisplay3.mDisplayId).isEqualTo(3)
+        assertThat(actualDisplay3.mWidth).isEqualTo(600f)
+        assertThat(actualDisplay3.mHeight).isEqualTo(200f)
+        assertThat(actualDisplay3.mPosition).isEqualTo(POSITION_RIGHT)
+        assertThat(actualDisplay3.mOffset).isEqualTo(400f)
+        assertThat(actualDisplay3.mChildren).isEmpty()
+
+        val actualDisplay4 = actualDisplay2.mChildren[0]
+        assertThat(actualDisplay4.mDisplayId).isEqualTo(4)
+        assertThat(actualDisplay4.mWidth).isEqualTo(200f)
+        assertThat(actualDisplay4.mHeight).isEqualTo(600f)
+        assertThat(actualDisplay4.mPosition).isEqualTo(POSITION_RIGHT)
+        assertThat(actualDisplay4.mOffset).isEqualTo(0f)
+        assertThat(actualDisplay4.mChildren).isEmpty()
+    }
+
+    @Test
+    fun normalization_moveDisplayWithoutReparenting() {
+        val display1 = DisplayTopology.TreeNode(/* displayId= */ 1, /* width= */ 200f,
+            /* height= */ 600f, /* position= */ null, /* offset= */ 0f)
+        topology.mRoot = display1
+
+        val display2 = DisplayTopology.TreeNode(/* displayId= */ 2, /* width= */ 200f,
+            /* height= */ 600f, POSITION_RIGHT, /* offset= */ 0f)
+        display1.mChildren.add(display2)
+
+        val primaryDisplayId = 3
+        val display3 = DisplayTopology.TreeNode(primaryDisplayId, /* width= */ 600f,
+            /* height= */ 200f, POSITION_RIGHT, /* offset= */ 10f)
+        display1.mChildren.add(display3)
+        topology.mPrimaryDisplayId = primaryDisplayId
+
+        val display4 = DisplayTopology.TreeNode(/* displayId= */ 4, /* width= */ 200f,
+            /* height= */ 600f, POSITION_RIGHT, /* offset= */ 0f)
+        display2.mChildren.add(display4)
+
+        // Display 3 becomes a child of display 2. Display 4 gets moved without changing its parent.
+        topology.normalize()
+
+        assertThat(topology.mPrimaryDisplayId).isEqualTo(primaryDisplayId)
+
+        val actualDisplay1 = topology.mRoot!!
+        assertThat(actualDisplay1.mDisplayId).isEqualTo(1)
+        assertThat(actualDisplay1.mWidth).isEqualTo(200f)
+        assertThat(actualDisplay1.mHeight).isEqualTo(600f)
+        assertThat(actualDisplay1.mChildren).hasSize(1)
+
+        val actualDisplay2 = actualDisplay1.mChildren[0]
+        assertThat(actualDisplay2.mDisplayId).isEqualTo(2)
+        assertThat(actualDisplay2.mWidth).isEqualTo(200f)
+        assertThat(actualDisplay2.mHeight).isEqualTo(600f)
+        assertThat(actualDisplay2.mPosition).isEqualTo(POSITION_RIGHT)
+        assertThat(actualDisplay2.mOffset).isEqualTo(0f)
+        assertThat(actualDisplay2.mChildren).hasSize(2)
+
+        val actualDisplay3 = actualDisplay2.mChildren[1]
+        assertThat(actualDisplay3.mDisplayId).isEqualTo(3)
+        assertThat(actualDisplay3.mWidth).isEqualTo(600f)
+        assertThat(actualDisplay3.mHeight).isEqualTo(200f)
+        assertThat(actualDisplay3.mPosition).isEqualTo(POSITION_RIGHT)
+        assertThat(actualDisplay3.mOffset).isEqualTo(10f)
+        assertThat(actualDisplay3.mChildren).isEmpty()
+
+        val actualDisplay4 = actualDisplay2.mChildren[0]
+        assertThat(actualDisplay4.mDisplayId).isEqualTo(4)
+        assertThat(actualDisplay4.mWidth).isEqualTo(200f)
+        assertThat(actualDisplay4.mHeight).isEqualTo(600f)
+        assertThat(actualDisplay4.mPosition).isEqualTo(POSITION_RIGHT)
+        assertThat(actualDisplay4.mOffset).isEqualTo(210f)
+        assertThat(actualDisplay4.mChildren).isEmpty()
+    }
+
+    @Test
+    fun normalization_moveDisplayWithoutReparenting_offsetOutOfBounds() {
+        val display1 = DisplayTopology.TreeNode(/* displayId= */ 1, /* width= */ 200f,
+            /* height= */ 50f, /* position= */ null, /* offset= */ 0f)
+        topology.mRoot = display1
+
+        val display2 = DisplayTopology.TreeNode(/* displayId= */ 2, /* width= */ 600f,
+            /* height= */ 200f, POSITION_RIGHT, /* offset= */ 0f)
+        display1.mChildren.add(display2)
+
+        val primaryDisplayId = 3
+        val display3 = DisplayTopology.TreeNode(primaryDisplayId, /* width= */ 600f,
+            /* height= */ 200f, POSITION_RIGHT, /* offset= */ 10f)
+        display1.mChildren.add(display3)
+        topology.mPrimaryDisplayId = primaryDisplayId
+
+        // Display 3 gets moved and its left side is still on the same line as the right side
+        // of Display 1, but it no longer touches it (the offset is out of bounds), so Display 2
+        // becomes its new parent.
+        topology.normalize()
+
+        assertThat(topology.mPrimaryDisplayId).isEqualTo(primaryDisplayId)
+
+        val actualDisplay1 = topology.mRoot!!
+        assertThat(actualDisplay1.mDisplayId).isEqualTo(1)
+        assertThat(actualDisplay1.mWidth).isEqualTo(200f)
+        assertThat(actualDisplay1.mHeight).isEqualTo(50f)
+        assertThat(actualDisplay1.mChildren).hasSize(1)
+
+        val actualDisplay2 = actualDisplay1.mChildren[0]
+        assertThat(actualDisplay2.mDisplayId).isEqualTo(2)
+        assertThat(actualDisplay2.mWidth).isEqualTo(600f)
+        assertThat(actualDisplay2.mHeight).isEqualTo(200f)
+        assertThat(actualDisplay2.mPosition).isEqualTo(POSITION_RIGHT)
+        assertThat(actualDisplay2.mOffset).isEqualTo(0f)
+        assertThat(actualDisplay2.mChildren).hasSize(1)
+
+        val actualDisplay3 = actualDisplay2.mChildren[0]
+        assertThat(actualDisplay3.mDisplayId).isEqualTo(3)
+        assertThat(actualDisplay3.mWidth).isEqualTo(600f)
+        assertThat(actualDisplay3.mHeight).isEqualTo(200f)
+        assertThat(actualDisplay3.mPosition).isEqualTo(POSITION_BOTTOM)
+        assertThat(actualDisplay3.mOffset).isEqualTo(0f)
+        assertThat(actualDisplay3.mChildren).isEmpty()
+    }
+
+    @Test
+    fun normalization_moveAndReparentDisplay() {
+        val display1 = DisplayTopology.TreeNode(/* displayId= */ 1, /* width= */ 200f,
+            /* height= */ 600f, /* position= */ null, /* offset= */ 0f)
+        topology.mRoot = display1
+
+        val display2 = DisplayTopology.TreeNode(/* displayId= */ 2, /* width= */ 200f,
+            /* height= */ 600f, POSITION_RIGHT, /* offset= */ 0f)
+        display1.mChildren.add(display2)
+
+        val primaryDisplayId = 3
+        val display3 = DisplayTopology.TreeNode(primaryDisplayId, /* width= */ 600f,
+            /* height= */ 200f, POSITION_RIGHT, /* offset= */ 400f)
+        display1.mChildren.add(display3)
+        topology.mPrimaryDisplayId = primaryDisplayId
+
+        val display4 = DisplayTopology.TreeNode(/* displayId= */ 4, /* width= */ 200f,
+            /* height= */ 600f, POSITION_RIGHT, /* offset= */ 0f)
+        display2.mChildren.add(display4)
+
+        topology.normalize()
+
+        assertThat(topology.mPrimaryDisplayId).isEqualTo(primaryDisplayId)
+
+        val actualDisplay1 = topology.mRoot!!
+        assertThat(actualDisplay1.mDisplayId).isEqualTo(1)
+        assertThat(actualDisplay1.mWidth).isEqualTo(200f)
+        assertThat(actualDisplay1.mHeight).isEqualTo(600f)
+        assertThat(actualDisplay1.mChildren).hasSize(1)
+
+        val actualDisplay2 = actualDisplay1.mChildren[0]
+        assertThat(actualDisplay2.mDisplayId).isEqualTo(2)
+        assertThat(actualDisplay2.mWidth).isEqualTo(200f)
+        assertThat(actualDisplay2.mHeight).isEqualTo(600f)
+        assertThat(actualDisplay2.mPosition).isEqualTo(POSITION_RIGHT)
+        assertThat(actualDisplay2.mOffset).isEqualTo(0f)
+        assertThat(actualDisplay2.mChildren).hasSize(1)
+
+        val actualDisplay3 = actualDisplay2.mChildren[0]
+        assertThat(actualDisplay3.mDisplayId).isEqualTo(3)
+        assertThat(actualDisplay3.mWidth).isEqualTo(600f)
+        assertThat(actualDisplay3.mHeight).isEqualTo(200f)
+        assertThat(actualDisplay3.mPosition).isEqualTo(POSITION_RIGHT)
+        assertThat(actualDisplay3.mOffset).isEqualTo(400f)
+        assertThat(actualDisplay3.mChildren).hasSize(1)
+
+        val actualDisplay4 = actualDisplay3.mChildren[0]
+        assertThat(actualDisplay4.mDisplayId).isEqualTo(4)
+        assertThat(actualDisplay4.mWidth).isEqualTo(200f)
+        assertThat(actualDisplay4.mHeight).isEqualTo(600f)
+        assertThat(actualDisplay4.mPosition).isEqualTo(POSITION_RIGHT)
+        assertThat(actualDisplay4.mOffset).isEqualTo(-400f)
+        assertThat(actualDisplay4.mChildren).isEmpty()
+    }
 }
\ No newline at end of file
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BaseBroadcastQueueTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BaseBroadcastQueueTest.java
index 5861917..d602660 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/BaseBroadcastQueueTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/BaseBroadcastQueueTest.java
@@ -23,6 +23,7 @@
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.spy;
 
 import android.annotation.NonNull;
@@ -52,11 +53,11 @@
 import com.android.server.DropBoxManagerInternal;
 import com.android.server.LocalServices;
 import com.android.server.appop.AppOpsService;
+import com.android.server.compat.PlatformCompat;
 import com.android.server.wm.ActivityTaskManagerService;
 
 import org.junit.Rule;
 import org.mockito.Mock;
-import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
 
 import java.io.File;
@@ -164,7 +165,7 @@
         realAms.mActivityTaskManager = new ActivityTaskManagerService(mContext);
         realAms.mActivityTaskManager.initialize(null, null, mContext.getMainLooper());
         realAms.mAtmInternal = spy(realAms.mActivityTaskManager.getAtmInternal());
-        realAms.mOomAdjuster.mCachedAppOptimizer = Mockito.mock(CachedAppOptimizer.class);
+        realAms.mOomAdjuster.mCachedAppOptimizer = mock(CachedAppOptimizer.class);
         realAms.mOomAdjuster = spy(realAms.mOomAdjuster);
         ExtendedMockito.doNothing().when(() -> ProcessList.setOomAdj(anyInt(), anyInt(), anyInt()));
         realAms.mPackageManagerInt = mPackageManagerInt;
@@ -286,7 +287,8 @@
         filter.setPriority(priority);
         final BroadcastFilter res = new BroadcastFilter(filter, receiverList,
                 receiverList.app.info.packageName, null, null, null, receiverList.uid,
-                receiverList.userId, false, false, true);
+                receiverList.userId, false, false, true,
+                mock(PlatformCompat.class));
         receiverList.add(res);
         return res;
     }
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastFilterTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastFilterTest.java
new file mode 100644
index 0000000..e977a7d
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastFilterTest.java
@@ -0,0 +1,220 @@
+/*
+ * 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.am;
+
+import static android.content.IntentFilter.SYSTEM_HIGH_PRIORITY;
+import static android.content.IntentFilter.SYSTEM_LOW_PRIORITY;
+
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doReturn;
+
+import android.os.Process;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
+import android.util.Pair;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.server.compat.PlatformCompat;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+public class BroadcastFilterTest {
+    private static final int TEST_APP_UID = Process.FIRST_APPLICATION_UID + 42;
+
+    @Rule
+    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
+    @Mock
+    PlatformCompat mPlatformCompat;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_RESTRICT_PRIORITY_VALUES)
+    public void testCalculateAdjustedPriority() {
+        doReturn(true).when(mPlatformCompat).isChangeEnabledByUidInternalNoLogging(
+                eq(BroadcastFilter.CHANGE_RESTRICT_PRIORITY_VALUES), anyInt());
+
+        {
+            // Pairs of {initial-priority, expected-adjusted-priority}
+            final Pair<Integer, Integer>[] priorities = new Pair[] {
+                    Pair.create(SYSTEM_HIGH_PRIORITY, SYSTEM_HIGH_PRIORITY),
+                    Pair.create(SYSTEM_LOW_PRIORITY, SYSTEM_LOW_PRIORITY),
+                    Pair.create(SYSTEM_HIGH_PRIORITY + 1, SYSTEM_HIGH_PRIORITY + 1),
+                    Pair.create(SYSTEM_LOW_PRIORITY - 1, SYSTEM_LOW_PRIORITY - 1),
+                    Pair.create(SYSTEM_HIGH_PRIORITY - 2, SYSTEM_HIGH_PRIORITY - 2),
+                    Pair.create(SYSTEM_LOW_PRIORITY + 2, SYSTEM_LOW_PRIORITY + 2)
+            };
+            for (Pair<Integer, Integer> priorityPair : priorities) {
+                assertAdjustedPriorityForSystemUid(priorityPair.first, priorityPair.second);
+            }
+        }
+
+        {
+            // Pairs of {initial-priority, expected-adjusted-priority}
+            final Pair<Integer, Integer>[] priorities = new Pair[] {
+                    Pair.create(SYSTEM_HIGH_PRIORITY, SYSTEM_HIGH_PRIORITY - 1),
+                    Pair.create(SYSTEM_LOW_PRIORITY, SYSTEM_LOW_PRIORITY + 1),
+                    Pair.create(SYSTEM_HIGH_PRIORITY + 1, SYSTEM_HIGH_PRIORITY - 1),
+                    Pair.create(SYSTEM_LOW_PRIORITY - 1, SYSTEM_LOW_PRIORITY + 1),
+                    Pair.create(SYSTEM_HIGH_PRIORITY - 2, SYSTEM_HIGH_PRIORITY - 2),
+                    Pair.create(SYSTEM_LOW_PRIORITY + 2, SYSTEM_LOW_PRIORITY + 2)
+            };
+            for (Pair<Integer, Integer> priorityPair : priorities) {
+                assertAdjustedPriorityForAppUid(priorityPair.first, priorityPair.second);
+            }
+        }
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_RESTRICT_PRIORITY_VALUES)
+    public void testCalculateAdjustedPriority_withChangeIdDisabled() {
+        doReturn(false).when(mPlatformCompat).isChangeEnabledByUidInternalNoLogging(
+                eq(BroadcastFilter.CHANGE_RESTRICT_PRIORITY_VALUES), anyInt());
+
+        {
+            // Pairs of {initial-priority, expected-adjusted-priority}
+            final Pair<Integer, Integer>[] priorities = new Pair[] {
+                    Pair.create(SYSTEM_HIGH_PRIORITY, SYSTEM_HIGH_PRIORITY),
+                    Pair.create(SYSTEM_LOW_PRIORITY, SYSTEM_LOW_PRIORITY),
+                    Pair.create(SYSTEM_HIGH_PRIORITY + 1, SYSTEM_HIGH_PRIORITY + 1),
+                    Pair.create(SYSTEM_LOW_PRIORITY - 1, SYSTEM_LOW_PRIORITY - 1),
+                    Pair.create(SYSTEM_HIGH_PRIORITY - 2, SYSTEM_HIGH_PRIORITY - 2),
+                    Pair.create(SYSTEM_LOW_PRIORITY + 2, SYSTEM_LOW_PRIORITY + 2)
+            };
+            for (Pair<Integer, Integer> priorityPair : priorities) {
+                assertAdjustedPriorityForSystemUid(priorityPair.first, priorityPair.second);
+            }
+        }
+
+        {
+            // Pairs of {initial-priority, expected-adjusted-priority}
+            final Pair<Integer, Integer>[] priorities = new Pair[] {
+                    Pair.create(SYSTEM_HIGH_PRIORITY, SYSTEM_HIGH_PRIORITY),
+                    Pair.create(SYSTEM_LOW_PRIORITY, SYSTEM_LOW_PRIORITY),
+                    Pair.create(SYSTEM_HIGH_PRIORITY + 1, SYSTEM_HIGH_PRIORITY + 1),
+                    Pair.create(SYSTEM_LOW_PRIORITY - 1, SYSTEM_LOW_PRIORITY - 1),
+                    Pair.create(SYSTEM_HIGH_PRIORITY - 2, SYSTEM_HIGH_PRIORITY - 2),
+                    Pair.create(SYSTEM_LOW_PRIORITY + 2, SYSTEM_LOW_PRIORITY + 2)
+            };
+            for (Pair<Integer, Integer> priorityPair : priorities) {
+                assertAdjustedPriorityForAppUid(priorityPair.first, priorityPair.second);
+            }
+        }
+    }
+
+    @Test
+    @DisableFlags(Flags.FLAG_RESTRICT_PRIORITY_VALUES)
+    public void testCalculateAdjustedPriority_withFlagDisabled() {
+        doReturn(true).when(mPlatformCompat).isChangeEnabledByUidInternalNoLogging(
+                eq(BroadcastFilter.CHANGE_RESTRICT_PRIORITY_VALUES), anyInt());
+
+        {
+            // Pairs of {initial-priority, expected-adjusted-priority}
+            final Pair<Integer, Integer>[] priorities = new Pair[] {
+                    Pair.create(SYSTEM_HIGH_PRIORITY, SYSTEM_HIGH_PRIORITY),
+                    Pair.create(SYSTEM_LOW_PRIORITY, SYSTEM_LOW_PRIORITY),
+                    Pair.create(SYSTEM_HIGH_PRIORITY + 1, SYSTEM_HIGH_PRIORITY + 1),
+                    Pair.create(SYSTEM_LOW_PRIORITY - 1, SYSTEM_LOW_PRIORITY - 1),
+                    Pair.create(SYSTEM_HIGH_PRIORITY - 2, SYSTEM_HIGH_PRIORITY - 2),
+                    Pair.create(SYSTEM_LOW_PRIORITY + 2, SYSTEM_LOW_PRIORITY + 2)
+            };
+            for (Pair<Integer, Integer> priorityPair : priorities) {
+                assertAdjustedPriorityForSystemUid(priorityPair.first, priorityPair.second);
+            }
+        }
+
+        {
+            // Pairs of {initial-priority, expected-adjusted-priority}
+            final Pair<Integer, Integer>[] priorities = new Pair[] {
+                    Pair.create(SYSTEM_HIGH_PRIORITY, SYSTEM_HIGH_PRIORITY),
+                    Pair.create(SYSTEM_LOW_PRIORITY, SYSTEM_LOW_PRIORITY),
+                    Pair.create(SYSTEM_HIGH_PRIORITY + 1, SYSTEM_HIGH_PRIORITY + 1),
+                    Pair.create(SYSTEM_LOW_PRIORITY - 1, SYSTEM_LOW_PRIORITY - 1),
+                    Pair.create(SYSTEM_HIGH_PRIORITY - 2, SYSTEM_HIGH_PRIORITY - 2),
+                    Pair.create(SYSTEM_LOW_PRIORITY + 2, SYSTEM_LOW_PRIORITY + 2)
+            };
+            for (Pair<Integer, Integer> priorityPair : priorities) {
+                assertAdjustedPriorityForAppUid(priorityPair.first, priorityPair.second);
+            }
+        }
+    }
+
+    @Test
+    @DisableFlags(Flags.FLAG_RESTRICT_PRIORITY_VALUES)
+    public void testCalculateAdjustedPriority_withFlagDisabled_withChangeIdDisabled() {
+        doReturn(false).when(mPlatformCompat).isChangeEnabledByUidInternalNoLogging(
+                eq(BroadcastFilter.CHANGE_RESTRICT_PRIORITY_VALUES), anyInt());
+
+        {
+            // Pairs of {initial-priority, expected-adjusted-priority}
+            final Pair<Integer, Integer>[] priorities = new Pair[] {
+                    Pair.create(SYSTEM_HIGH_PRIORITY, SYSTEM_HIGH_PRIORITY),
+                    Pair.create(SYSTEM_LOW_PRIORITY, SYSTEM_LOW_PRIORITY),
+                    Pair.create(SYSTEM_HIGH_PRIORITY + 1, SYSTEM_HIGH_PRIORITY + 1),
+                    Pair.create(SYSTEM_LOW_PRIORITY - 1, SYSTEM_LOW_PRIORITY - 1),
+                    Pair.create(SYSTEM_HIGH_PRIORITY - 2, SYSTEM_HIGH_PRIORITY - 2),
+                    Pair.create(SYSTEM_LOW_PRIORITY + 2, SYSTEM_LOW_PRIORITY + 2)
+            };
+            for (Pair<Integer, Integer> priorityPair : priorities) {
+                assertAdjustedPriorityForSystemUid(priorityPair.first, priorityPair.second);
+            }
+        }
+
+        {
+            // Pairs of {initial-priority, expected-adjusted-priority}
+            final Pair<Integer, Integer>[] priorities = new Pair[] {
+                    Pair.create(SYSTEM_HIGH_PRIORITY, SYSTEM_HIGH_PRIORITY),
+                    Pair.create(SYSTEM_LOW_PRIORITY, SYSTEM_LOW_PRIORITY),
+                    Pair.create(SYSTEM_HIGH_PRIORITY + 1, SYSTEM_HIGH_PRIORITY + 1),
+                    Pair.create(SYSTEM_LOW_PRIORITY - 1, SYSTEM_LOW_PRIORITY - 1),
+                    Pair.create(SYSTEM_HIGH_PRIORITY - 2, SYSTEM_HIGH_PRIORITY - 2),
+                    Pair.create(SYSTEM_LOW_PRIORITY + 2, SYSTEM_LOW_PRIORITY + 2)
+            };
+            for (Pair<Integer, Integer> priorityPair : priorities) {
+                assertAdjustedPriorityForAppUid(priorityPair.first, priorityPair.second);
+            }
+        }
+    }
+
+    private void assertAdjustedPriorityForSystemUid(int priority, int expectedAdjustedPriority) {
+        assertAdjustedPriority(Process.SYSTEM_UID, priority, expectedAdjustedPriority);
+    }
+
+    private void assertAdjustedPriorityForAppUid(int priority, int expectedAdjustedPriority) {
+        assertAdjustedPriority(TEST_APP_UID, priority, expectedAdjustedPriority);
+    }
+
+    private void assertAdjustedPriority(int owningUid, int priority, int expectedAdjustedPriority) {
+        final String errorMsg = String.format("owner=%d; actualPriority=%d; expectedPriority=%d",
+                owningUid, priority, expectedAdjustedPriority);
+        assertWithMessage(errorMsg).that(BroadcastFilter.calculateAdjustedPriority(
+                owningUid, priority, mPlatformCompat)).isEqualTo(expectedAdjustedPriority);
+    }
+}
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 6ab72cd..b005358 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
@@ -228,6 +228,8 @@
                 mock(BatteryStatsService.class));
         setFieldValue(ActivityManagerService.class, mService, "mInjector",
                 new ActivityManagerService.Injector(mContext));
+        setFieldValue(ActivityManagerService.class, mService, "mPhantomProcessList",
+                new PhantomProcessList(mService));
         doReturn(mock(AppOpsManager.class)).when(mService).getAppOpsManager();
         doCallRealMethod().when(mService).enqueueOomAdjTargetLocked(any(ProcessRecord.class));
         doCallRealMethod().when(mService).updateOomAdjPendingTargetsLocked(OOM_ADJ_REASON_ACTIVITY);
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/ServiceBindingOomAdjPolicyTest.java b/services/tests/mockingservicestests/src/com/android/server/am/ServiceBindingOomAdjPolicyTest.java
index 59302ee..2988c77 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/ServiceBindingOomAdjPolicyTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/ServiceBindingOomAdjPolicyTest.java
@@ -22,6 +22,7 @@
 import static android.app.ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE;
 import static android.app.ActivityManager.PROCESS_STATE_HOME;
 import static android.app.ActivityManager.PROCESS_STATE_SERVICE;
+import static android.content.Context.BIND_ALLOW_OOM_MANAGEMENT;
 import static android.content.Context.BIND_AUTO_CREATE;
 import static android.content.Context.BIND_INCLUDE_CAPABILITIES;
 import static android.content.Context.BIND_WAIVE_PRIORITY;
@@ -29,6 +30,7 @@
 import static android.os.UserHandle.USER_SYSTEM;
 import static android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT;
 
+import static com.android.server.am.ProcessCachedOptimizerRecord.SHOULD_NOT_FREEZE_REASON_NONE;
 import static com.android.server.am.ProcessList.CACHED_APP_MIN_ADJ;
 import static com.android.server.am.ProcessList.HOME_APP_ADJ;
 import static com.android.server.am.ProcessList.PERCEPTIBLE_APP_ADJ;
@@ -63,6 +65,10 @@
 import android.os.HandlerThread;
 import android.os.IBinder;
 import android.platform.test.annotations.Presubmit;
+import android.platform.test.annotations.RequiresFlagsDisabled;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
 import android.platform.test.flag.junit.SetFlagsRule;
 
 import androidx.test.platform.app.InstrumentationRegistry;
@@ -114,6 +120,8 @@
     public final ServiceThreadRule mServiceThreadRule = new ServiceThreadRule();
     @Rule
     public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(DEVICE_DEFAULT);
+    @Rule
+    public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
 
     private Context mContext;
     private HandlerThread mHandlerThread;
@@ -317,6 +325,256 @@
     }
 
     @Test
+    @RequiresFlagsEnabled(com.android.server.am.Flags.FLAG_UNFREEZE_BIND_POLICY_FIX)
+    public void testServiceDistinctBindingOomAdjShouldNotFreeze() throws Exception {
+        // Enable the flags.
+        mSetFlagsRule.enableFlags(Flags.FLAG_SERVICE_BINDING_OOM_ADJ_POLICY);
+
+        // Verify that there should be at least 1 oom adj update
+        // because the shouldNotFreeze state needs to be propagated.
+        performTestServiceDistinctBindingOomAdj(TEST_APP1_PID, TEST_APP1_UID,
+                PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ,
+                PROCESS_CAPABILITY_NONE, TEST_APP1_NAME,
+                (app) -> {
+                    this.setHasForegroundServices(app);
+                    this.setAllowListed(app);
+                },
+                TEST_APP2_PID, TEST_APP2_UID, PROCESS_STATE_HOME,
+                HOME_APP_ADJ, PROCESS_CAPABILITY_NONE, TEST_APP2_NAME, TEST_SERVICE2_NAME,
+                this::setHomeProcess,
+                BIND_AUTO_CREATE,
+                atLeastOnce(), atLeastOnce());
+
+        // Verify that there should be at least 1 oom adj update
+        // because the shouldNotFreeze state needs to be propagated.
+        performTestServiceDistinctBindingOomAdj(TEST_APP1_PID, TEST_APP1_UID,
+                PROCESS_STATE_HOME, HOME_APP_ADJ, PROCESS_CAPABILITY_NONE, TEST_APP1_NAME,
+                (app) -> {
+                    this.setHomeProcess(app);
+                    this.setAllowListed(app);
+                },
+                TEST_APP2_PID, TEST_APP2_UID, PROCESS_STATE_FOREGROUND_SERVICE,
+                PERCEPTIBLE_APP_ADJ, PROCESS_CAPABILITY_NONE, TEST_APP2_NAME, TEST_SERVICE2_NAME,
+                this::setHasForegroundServices,
+                BIND_AUTO_CREATE,
+                atLeastOnce(), atLeastOnce());
+
+        // Verify that there should be at least 1 oom adj update
+        // because the client is more important (regardless of shouldNotFreeze state).
+        performTestServiceDistinctBindingOomAdj(TEST_APP1_PID, TEST_APP1_UID,
+                PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ,
+                PROCESS_CAPABILITY_NONE, TEST_APP1_NAME,
+                (app) -> {
+                    this.setHasForegroundServices(app);
+                    this.setAllowListed(app);
+                },
+                TEST_APP2_PID, TEST_APP2_UID, PROCESS_STATE_HOME,
+                HOME_APP_ADJ, PROCESS_CAPABILITY_NONE, TEST_APP2_NAME, TEST_SERVICE2_NAME,
+                (app) -> {
+                    this.setHomeProcess(app);
+                    this.setAllowListed(app);
+                },
+                BIND_AUTO_CREATE,
+                atLeastOnce(), atLeastOnce());
+
+        // Verify that there should be 0 oom adj update for binding
+        // because setShouldNotFreeze is already set
+        // but for the unbinding must update in case the binding could be the source of the
+        // shouldNotFreeze.
+        performTestServiceDistinctBindingOomAdj(TEST_APP1_PID, TEST_APP1_UID,
+                PROCESS_STATE_HOME, HOME_APP_ADJ, PROCESS_CAPABILITY_NONE, TEST_APP1_NAME,
+                (app) -> {
+                    this.setHomeProcess(app);
+                    this.setAllowListed(app);
+                },
+                TEST_APP2_PID, TEST_APP2_UID, PROCESS_STATE_FOREGROUND_SERVICE,
+                PERCEPTIBLE_APP_ADJ, PROCESS_CAPABILITY_NONE, TEST_APP2_NAME, TEST_SERVICE2_NAME,
+                (app) -> {
+                    this.setHasForegroundServices(app);
+                    this.setAllowListed(app);
+                },
+                BIND_AUTO_CREATE,
+                never(), atLeastOnce());
+
+
+        // Disable the flags.
+        mSetFlagsRule.disableFlags(Flags.FLAG_SERVICE_BINDING_OOM_ADJ_POLICY);
+
+        // Verify that there should be at least 1 oom adj update
+        // because the client is more important.
+        performTestServiceDistinctBindingOomAdj(TEST_APP1_PID, TEST_APP1_UID,
+                PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ,
+                PROCESS_CAPABILITY_NONE, TEST_APP1_NAME,
+                (app) -> {
+                    this.setHasForegroundServices(app);
+                    this.setAllowListed(app);
+                },
+                TEST_APP2_PID, TEST_APP2_UID, PROCESS_STATE_HOME,
+                HOME_APP_ADJ, PROCESS_CAPABILITY_NONE, TEST_APP2_NAME, TEST_SERVICE2_NAME,
+                this::setHomeProcess,
+                BIND_AUTO_CREATE,
+                atLeastOnce(), atLeastOnce());
+    }
+
+    @Test
+    @RequiresFlagsEnabled(com.android.server.am.Flags.FLAG_UNFREEZE_BIND_POLICY_FIX)
+    public void testServiceDistinctBindingOomAdjAllowOomManagement() throws Exception {
+        // Enable the flags.
+        mSetFlagsRule.enableFlags(Flags.FLAG_SERVICE_BINDING_OOM_ADJ_POLICY);
+
+        // Verify that there should be at least 1 oom adj update
+        // because BIND_ALLOW_OOM_MANAGEMENT sets the shouldNotFreeze state which needs to be
+        // propagated.
+        performTestServiceDistinctBindingOomAdj(TEST_APP1_PID, TEST_APP1_UID,
+                PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ,
+                PROCESS_CAPABILITY_NONE, TEST_APP1_NAME,
+                this::setHasForegroundServices,
+                TEST_APP2_PID, TEST_APP2_UID, PROCESS_STATE_HOME,
+                HOME_APP_ADJ, PROCESS_CAPABILITY_NONE, TEST_APP2_NAME, TEST_SERVICE2_NAME,
+                this::setHomeProcess,
+                BIND_AUTO_CREATE | BIND_ALLOW_OOM_MANAGEMENT,
+                atLeastOnce(), atLeastOnce());
+
+        // Verify that there should be at least 1 oom adj update
+        // because BIND_ALLOW_OOM_MANAGEMENT sets the shouldNotFreeze state which needs to be
+        // propagated.
+        performTestServiceDistinctBindingOomAdj(TEST_APP1_PID, TEST_APP1_UID,
+                PROCESS_STATE_HOME, HOME_APP_ADJ, PROCESS_CAPABILITY_NONE, TEST_APP1_NAME,
+                this::setHomeProcess,
+                TEST_APP2_PID, TEST_APP2_UID, PROCESS_STATE_FOREGROUND_SERVICE,
+                PERCEPTIBLE_APP_ADJ, PROCESS_CAPABILITY_NONE, TEST_APP2_NAME, TEST_SERVICE2_NAME,
+                this::setHasForegroundServices,
+                BIND_AUTO_CREATE | BIND_ALLOW_OOM_MANAGEMENT,
+                atLeastOnce(), atLeastOnce());
+
+        // Verify that there should be at least 1 oom adj update
+        // because the client is more important (regardless of BIND_ALLOW_OOM_MANAGEMENT).
+        performTestServiceDistinctBindingOomAdj(TEST_APP1_PID, TEST_APP1_UID,
+                PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ,
+                PROCESS_CAPABILITY_NONE, TEST_APP1_NAME,
+                this::setHasForegroundServices,
+                TEST_APP2_PID, TEST_APP2_UID, PROCESS_STATE_HOME,
+                HOME_APP_ADJ, PROCESS_CAPABILITY_NONE, TEST_APP2_NAME, TEST_SERVICE2_NAME,
+                (app) -> {
+                    this.setHomeProcess(app);
+                    this.setAllowListed(app);
+                },
+                BIND_AUTO_CREATE | BIND_ALLOW_OOM_MANAGEMENT,
+                atLeastOnce(), atLeastOnce());
+
+        // Verify that there should be 0 oom adj update for binding
+        // because setShouldNotFreeze is already set
+        // but for the unbinding must update in case the BIND_ALLOW_OOM_MANAGEMENT maintaining the
+        // shouldNotFreeze.
+        performTestServiceDistinctBindingOomAdj(TEST_APP1_PID, TEST_APP1_UID,
+                PROCESS_STATE_HOME, HOME_APP_ADJ, PROCESS_CAPABILITY_NONE, TEST_APP1_NAME,
+                this::setHomeProcess,
+                TEST_APP2_PID, TEST_APP2_UID, PROCESS_STATE_FOREGROUND_SERVICE,
+                PERCEPTIBLE_APP_ADJ, PROCESS_CAPABILITY_NONE, TEST_APP2_NAME, TEST_SERVICE2_NAME,
+                (app) -> {
+                    this.setHasForegroundServices(app);
+                    this.setAllowListed(app);
+                },
+                BIND_AUTO_CREATE | BIND_ALLOW_OOM_MANAGEMENT,
+                never(), atLeastOnce());
+
+
+        // Disable the flags.
+        mSetFlagsRule.disableFlags(Flags.FLAG_SERVICE_BINDING_OOM_ADJ_POLICY);
+
+        // Verify that there should be at least 1 oom adj update
+        // because the client is more important.
+        performTestServiceDistinctBindingOomAdj(TEST_APP1_PID, TEST_APP1_UID,
+                PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ,
+                PROCESS_CAPABILITY_NONE, TEST_APP1_NAME,
+                this::setHasForegroundServices,
+                TEST_APP2_PID, TEST_APP2_UID, PROCESS_STATE_HOME,
+                HOME_APP_ADJ, PROCESS_CAPABILITY_NONE, TEST_APP2_NAME, TEST_SERVICE2_NAME,
+                this::setHomeProcess,
+                BIND_AUTO_CREATE | BIND_ALLOW_OOM_MANAGEMENT,
+                atLeastOnce(), atLeastOnce());
+    }
+
+    @Test
+    @RequiresFlagsEnabled(com.android.server.am.Flags.FLAG_UNFREEZE_BIND_POLICY_FIX)
+    public void testServiceDistinctBindingOomAdjWaivePriority_propagateUnfreeze() throws Exception {
+        // Enable the flags.
+        mSetFlagsRule.enableFlags(Flags.FLAG_SERVICE_BINDING_OOM_ADJ_POLICY);
+
+        // Verify that there should be at least 1 oom adj update
+        // because BIND_WAIVE_PRIORITY sets the shouldNotFreeze state which needs to be propagated.
+        performTestServiceDistinctBindingOomAdj(TEST_APP1_PID, TEST_APP1_UID,
+                PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ,
+                PROCESS_CAPABILITY_NONE, TEST_APP1_NAME,
+                this::setHasForegroundServices,
+                TEST_APP2_PID, TEST_APP2_UID, PROCESS_STATE_HOME,
+                HOME_APP_ADJ, PROCESS_CAPABILITY_NONE, TEST_APP2_NAME, TEST_SERVICE2_NAME,
+                this::setHomeProcess,
+                BIND_AUTO_CREATE | BIND_WAIVE_PRIORITY,
+                atLeastOnce(), atLeastOnce());
+
+        // Verify that there should be at least 1 oom adj update
+        // because BIND_WAIVE_PRIORITY sets the shouldNotFreeze state which needs to be propagated.
+        performTestServiceDistinctBindingOomAdj(TEST_APP1_PID, TEST_APP1_UID,
+                PROCESS_STATE_HOME, HOME_APP_ADJ, PROCESS_CAPABILITY_NONE, TEST_APP1_NAME,
+                this::setHomeProcess,
+                TEST_APP2_PID, TEST_APP2_UID, PROCESS_STATE_FOREGROUND_SERVICE,
+                PERCEPTIBLE_APP_ADJ, PROCESS_CAPABILITY_NONE, TEST_APP2_NAME, TEST_SERVICE2_NAME,
+                this::setHasForegroundServices,
+                BIND_AUTO_CREATE | BIND_WAIVE_PRIORITY,
+                atLeastOnce(), atLeastOnce());
+
+        // Verify that there should be 0 oom adj update for binding
+        // because setShouldNotFreeze is already set
+        // but for the unbinding, because client is better than service, we can't skip it safely.
+        performTestServiceDistinctBindingOomAdj(TEST_APP1_PID, TEST_APP1_UID,
+                PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ,
+                PROCESS_CAPABILITY_NONE, TEST_APP1_NAME,
+                this::setHasForegroundServices,
+                TEST_APP2_PID, TEST_APP2_UID, PROCESS_STATE_HOME,
+                HOME_APP_ADJ, PROCESS_CAPABILITY_NONE, TEST_APP2_NAME, TEST_SERVICE2_NAME,
+                (app) -> {
+                    this.setHomeProcess(app);
+                    this.setAllowListed(app);
+                },
+                BIND_AUTO_CREATE | BIND_WAIVE_PRIORITY,
+                never(), atLeastOnce());
+
+        // Verify that there should be 0 oom adj update for binding
+        // because setShouldNotFreeze is already set
+        // but for the unbinding must update in case the BIND_WAIVE_PRIORITY maintaining the
+        // shouldNotFreeze.
+        performTestServiceDistinctBindingOomAdj(TEST_APP1_PID, TEST_APP1_UID,
+                PROCESS_STATE_HOME, HOME_APP_ADJ, PROCESS_CAPABILITY_NONE, TEST_APP1_NAME,
+                this::setHomeProcess,
+                TEST_APP2_PID, TEST_APP2_UID, PROCESS_STATE_FOREGROUND_SERVICE,
+                PERCEPTIBLE_APP_ADJ, PROCESS_CAPABILITY_NONE, TEST_APP2_NAME, TEST_SERVICE2_NAME,
+                (app) -> {
+                    this.setHasForegroundServices(app);
+                    this.setAllowListed(app);
+                },
+                BIND_AUTO_CREATE | BIND_WAIVE_PRIORITY,
+                never(), atLeastOnce());
+
+
+        // Disable the flags.
+        mSetFlagsRule.disableFlags(Flags.FLAG_SERVICE_BINDING_OOM_ADJ_POLICY);
+
+        // Verify that there should be at least 1 oom adj update
+        // because the client is more important.
+        performTestServiceDistinctBindingOomAdj(TEST_APP1_PID, TEST_APP1_UID,
+                PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ,
+                PROCESS_CAPABILITY_NONE, TEST_APP1_NAME,
+                this::setHasForegroundServices,
+                TEST_APP2_PID, TEST_APP2_UID, PROCESS_STATE_HOME,
+                HOME_APP_ADJ, PROCESS_CAPABILITY_NONE, TEST_APP2_NAME, TEST_SERVICE2_NAME,
+                this::setHomeProcess,
+                BIND_AUTO_CREATE | BIND_WAIVE_PRIORITY,
+                atLeastOnce(), atLeastOnce());
+    }
+
+    @Test
+    @RequiresFlagsDisabled(com.android.server.am.Flags.FLAG_UNFREEZE_BIND_POLICY_FIX)
     public void testServiceDistinctBindingOomAdjWaivePriority() throws Exception {
         // Enable the flags.
         mSetFlagsRule.enableFlags(Flags.FLAG_SERVICE_BINDING_OOM_ADJ_POLICY);
@@ -527,6 +785,15 @@
     }
 
     @SuppressWarnings("GuardedBy")
+    private void setAllowListed(ProcessRecord app) {
+        final UidRecord uidRec = mock(UidRecord.class);
+        app.setUidRecord(uidRec);
+        doReturn(true).when(uidRec).isCurAllowListed();
+
+        app.mOptRecord.setShouldNotFreeze(true, SHOULD_NOT_FREEZE_REASON_NONE, 1234);
+    }
+
+    @SuppressWarnings("GuardedBy")
     private ProcessRecord addProcessRecord(int pid, int uid, int procState, int adj, int cap,
                 String packageName) {
         final ApplicationThreadDeferred appThread = mock(ApplicationThreadDeferred.class);
@@ -574,8 +841,10 @@
             String processName, String packageName, ActivityManagerService ams) {
         final ProcessRecord app = ApplicationExitInfoTest.makeProcessRecord(pid, uid, packageUid,
                 definingUid, connectionGroup, procState, pss, rss, processName, packageName, ams);
+        app.mState.setCurRawProcState(procState);
         app.mState.setCurProcState(procState);
         app.mState.setSetProcState(procState);
+        app.mState.setCurRawAdj(adj);
         app.mState.setCurAdj(adj);
         app.mState.setSetAdj(adj);
         app.mState.setCurCapability(cap);
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/JobParametersTest.java b/services/tests/mockingservicestests/src/com/android/server/job/JobParametersTest.java
index c8e4f89..3b6c86e 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/JobParametersTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/JobParametersTest.java
@@ -16,7 +16,7 @@
 
 package com.android.server.job;
 
-import static android.app.job.Flags.FLAG_CLEANUP_EMPTY_JOBS;
+import static android.app.job.Flags.FLAG_HANDLE_ABANDONED_JOBS;
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
@@ -129,9 +129,9 @@
     }
 
     /** Test to verify that the JobParameters Cleaner is disabled */
-    @RequiresFlagsEnabled(FLAG_CLEANUP_EMPTY_JOBS)
+    @RequiresFlagsEnabled(FLAG_HANDLE_ABANDONED_JOBS)
     @Test
-    public void testCleanerWithLeakedJobCleanerDisabled_flagCleanupEmptyJobsEnabled() {
+    public void testCleanerWithLeakedJobCleanerDisabled_flagHandleAbandonedJobs() {
         // Inject real JobCallbackCleanup
         JobParameters jobParameters = JobParameters.CREATOR.createFromParcel(mMockParcel);
 
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
index b2fe138..d66bb00 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
@@ -1692,16 +1692,9 @@
                         3 * MINUTE_IN_MILLIS, 5), false);
         final long timeUntilQuotaConsumedMs = 7 * MINUTE_IN_MILLIS;
         JobStatus job = createJobStatus("testGetMaxJobExecutionTimeLocked", 0);
-        //noinspection deprecation
-        JobStatus jobDefIWF = createJobStatus("testGetMaxJobExecutionTimeLocked",
-                createJobInfoBuilder(1)
-                        .setImportantWhileForeground(true)
-                        .setPriority(JobInfo.PRIORITY_DEFAULT)
-                        .build());
         JobStatus jobHigh = createJobStatus("testGetMaxJobExecutionTimeLocked",
                 createJobInfoBuilder(2).setPriority(JobInfo.PRIORITY_HIGH).build());
         setStandbyBucket(RARE_INDEX, job);
-        setStandbyBucket(RARE_INDEX, jobDefIWF);
         setStandbyBucket(RARE_INDEX, jobHigh);
 
         setCharging();
@@ -1709,8 +1702,6 @@
             assertEquals(JobSchedulerService.Constants.DEFAULT_RUNTIME_FREE_QUOTA_MAX_LIMIT_MS,
                     mQuotaController.getMaxJobExecutionTimeMsLocked((job)));
             assertEquals(JobSchedulerService.Constants.DEFAULT_RUNTIME_FREE_QUOTA_MAX_LIMIT_MS,
-                    mQuotaController.getMaxJobExecutionTimeMsLocked((jobDefIWF)));
-            assertEquals(JobSchedulerService.Constants.DEFAULT_RUNTIME_FREE_QUOTA_MAX_LIMIT_MS,
                     mQuotaController.getMaxJobExecutionTimeMsLocked((jobHigh)));
         }
 
@@ -1720,8 +1711,6 @@
             assertEquals(timeUntilQuotaConsumedMs,
                     mQuotaController.getMaxJobExecutionTimeMsLocked((job)));
             assertEquals(JobSchedulerService.Constants.DEFAULT_RUNTIME_FREE_QUOTA_MAX_LIMIT_MS,
-                    mQuotaController.getMaxJobExecutionTimeMsLocked((jobDefIWF)));
-            assertEquals(JobSchedulerService.Constants.DEFAULT_RUNTIME_FREE_QUOTA_MAX_LIMIT_MS,
                     mQuotaController.getMaxJobExecutionTimeMsLocked((jobHigh)));
         }
 
@@ -1730,9 +1719,8 @@
         // Top-stared jobs are out of quota enforcement.
         setProcessState(ActivityManager.PROCESS_STATE_TOP);
         synchronized (mQuotaController.mLock) {
-            trackJobs(job, jobDefIWF, jobHigh);
+            trackJobs(job, jobHigh);
             mQuotaController.prepareForExecutionLocked(job);
-            mQuotaController.prepareForExecutionLocked(jobDefIWF);
             mQuotaController.prepareForExecutionLocked(jobHigh);
         }
         setProcessState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND);
@@ -1740,11 +1728,8 @@
             assertEquals(timeUntilQuotaConsumedMs,
                     mQuotaController.getMaxJobExecutionTimeMsLocked((job)));
             assertEquals(JobSchedulerService.Constants.DEFAULT_RUNTIME_FREE_QUOTA_MAX_LIMIT_MS,
-                    mQuotaController.getMaxJobExecutionTimeMsLocked((jobDefIWF)));
-            assertEquals(JobSchedulerService.Constants.DEFAULT_RUNTIME_FREE_QUOTA_MAX_LIMIT_MS,
                     mQuotaController.getMaxJobExecutionTimeMsLocked((jobHigh)));
             mQuotaController.maybeStopTrackingJobLocked(job, null);
-            mQuotaController.maybeStopTrackingJobLocked(jobDefIWF, null);
             mQuotaController.maybeStopTrackingJobLocked(jobHigh, null);
         }
 
@@ -1753,8 +1738,6 @@
             assertEquals(timeUntilQuotaConsumedMs,
                     mQuotaController.getMaxJobExecutionTimeMsLocked(job));
             assertEquals(timeUntilQuotaConsumedMs,
-                    mQuotaController.getMaxJobExecutionTimeMsLocked(jobDefIWF));
-            assertEquals(timeUntilQuotaConsumedMs,
                     mQuotaController.getMaxJobExecutionTimeMsLocked(jobHigh));
         }
 
@@ -1762,9 +1745,8 @@
         // Quota is enforced for top-started job after the process leaves TOP/BTOP state.
         setProcessState(ActivityManager.PROCESS_STATE_TOP);
         synchronized (mQuotaController.mLock) {
-            trackJobs(job, jobDefIWF, jobHigh);
+            trackJobs(job, jobHigh);
             mQuotaController.prepareForExecutionLocked(job);
-            mQuotaController.prepareForExecutionLocked(jobDefIWF);
             mQuotaController.prepareForExecutionLocked(jobHigh);
         }
         setProcessState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND);
@@ -1772,11 +1754,8 @@
             assertEquals(timeUntilQuotaConsumedMs,
                     mQuotaController.getMaxJobExecutionTimeMsLocked((job)));
             assertEquals(timeUntilQuotaConsumedMs,
-                    mQuotaController.getMaxJobExecutionTimeMsLocked((jobDefIWF)));
-            assertEquals(timeUntilQuotaConsumedMs,
                     mQuotaController.getMaxJobExecutionTimeMsLocked((jobHigh)));
             mQuotaController.maybeStopTrackingJobLocked(job, null);
-            mQuotaController.maybeStopTrackingJobLocked(jobDefIWF, null);
             mQuotaController.maybeStopTrackingJobLocked(jobHigh, null);
         }
 
@@ -1785,13 +1764,145 @@
             assertEquals(timeUntilQuotaConsumedMs,
                     mQuotaController.getMaxJobExecutionTimeMsLocked(job));
             assertEquals(timeUntilQuotaConsumedMs,
-                    mQuotaController.getMaxJobExecutionTimeMsLocked(jobDefIWF));
-            assertEquals(timeUntilQuotaConsumedMs,
                     mQuotaController.getMaxJobExecutionTimeMsLocked(jobHigh));
         }
     }
 
     @Test
+    public void testGetMaxJobExecutionTimeLocked_Regular_ImportantWhileForeground() {
+        mQuotaController.saveTimingSession(0, SOURCE_PACKAGE,
+                createTimingSession(sElapsedRealtimeClock.millis() - (6 * MINUTE_IN_MILLIS),
+                        3 * MINUTE_IN_MILLIS, 5), false);
+        final long timeUntilQuotaConsumedMs = 7 * MINUTE_IN_MILLIS;
+        JobStatus job = createJobStatus("testGetMaxJobExecutionTimeLocked", 0);
+        //noinspection deprecation
+        JobStatus jobDefIWF;
+        mSetFlagsRule.disableFlags(android.app.job.Flags.FLAG_IGNORE_IMPORTANT_WHILE_FOREGROUND);
+        jobDefIWF = createJobStatus("testGetMaxJobExecutionTimeLocked_IWF",
+                createJobInfoBuilder(1)
+                        .setImportantWhileForeground(true)
+                        .setPriority(JobInfo.PRIORITY_DEFAULT)
+                        .build());
+
+        setStandbyBucket(RARE_INDEX, jobDefIWF);
+        setCharging();
+        synchronized (mQuotaController.mLock) {
+            assertEquals(JobSchedulerService.Constants.DEFAULT_RUNTIME_FREE_QUOTA_MAX_LIMIT_MS,
+                    mQuotaController.getMaxJobExecutionTimeMsLocked((jobDefIWF)));
+        }
+
+        setDischarging();
+        setProcessState(getProcessStateQuotaFreeThreshold());
+        synchronized (mQuotaController.mLock) {
+            assertEquals(JobSchedulerService.Constants.DEFAULT_RUNTIME_FREE_QUOTA_MAX_LIMIT_MS,
+                    mQuotaController.getMaxJobExecutionTimeMsLocked((jobDefIWF)));
+        }
+
+        // Top-started job
+        mSetFlagsRule.disableFlags(Flags.FLAG_ENFORCE_QUOTA_POLICY_TO_TOP_STARTED_JOBS);
+        // Top-stared jobs are out of quota enforcement.
+        setProcessState(ActivityManager.PROCESS_STATE_TOP);
+        synchronized (mQuotaController.mLock) {
+            trackJobs(jobDefIWF);
+            mQuotaController.prepareForExecutionLocked(jobDefIWF);
+        }
+        setProcessState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND);
+        synchronized (mQuotaController.mLock) {
+            assertEquals(JobSchedulerService.Constants.DEFAULT_RUNTIME_FREE_QUOTA_MAX_LIMIT_MS,
+                    mQuotaController.getMaxJobExecutionTimeMsLocked((jobDefIWF)));
+            mQuotaController.maybeStopTrackingJobLocked(jobDefIWF, null);
+        }
+
+        setProcessState(ActivityManager.PROCESS_STATE_RECEIVER);
+        synchronized (mQuotaController.mLock) {
+            assertEquals(timeUntilQuotaConsumedMs,
+                    mQuotaController.getMaxJobExecutionTimeMsLocked(jobDefIWF));
+        }
+
+        mSetFlagsRule.enableFlags(Flags.FLAG_ENFORCE_QUOTA_POLICY_TO_TOP_STARTED_JOBS);
+        // Quota is enforced for top-started job after the process leaves TOP/BTOP state.
+        setProcessState(ActivityManager.PROCESS_STATE_TOP);
+        synchronized (mQuotaController.mLock) {
+            trackJobs(jobDefIWF);
+            mQuotaController.prepareForExecutionLocked(jobDefIWF);
+        }
+        setProcessState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND);
+        synchronized (mQuotaController.mLock) {
+            assertEquals(timeUntilQuotaConsumedMs,
+                    mQuotaController.getMaxJobExecutionTimeMsLocked((jobDefIWF)));
+            mQuotaController.maybeStopTrackingJobLocked(jobDefIWF, null);
+        }
+
+        setProcessState(ActivityManager.PROCESS_STATE_RECEIVER);
+        synchronized (mQuotaController.mLock) {
+            assertEquals(timeUntilQuotaConsumedMs,
+                    mQuotaController.getMaxJobExecutionTimeMsLocked(jobDefIWF));
+        }
+
+        mSetFlagsRule.enableFlags(android.app.job.Flags.FLAG_IGNORE_IMPORTANT_WHILE_FOREGROUND);
+        jobDefIWF = createJobStatus("testGetMaxJobExecutionTimeLocked_IWF",
+                createJobInfoBuilder(1)
+                        .setImportantWhileForeground(true)
+                        .setPriority(JobInfo.PRIORITY_DEFAULT)
+                        .build());
+
+        setStandbyBucket(RARE_INDEX, jobDefIWF);
+        setCharging();
+        synchronized (mQuotaController.mLock) {
+            assertEquals(JobSchedulerService.Constants.DEFAULT_RUNTIME_FREE_QUOTA_MAX_LIMIT_MS,
+                    mQuotaController.getMaxJobExecutionTimeMsLocked((jobDefIWF)));
+        }
+
+        setDischarging();
+        setProcessState(getProcessStateQuotaFreeThreshold());
+        synchronized (mQuotaController.mLock) {
+            assertEquals(timeUntilQuotaConsumedMs,
+                    mQuotaController.getMaxJobExecutionTimeMsLocked((jobDefIWF)));
+        }
+
+        // Top-started job
+        mSetFlagsRule.disableFlags(Flags.FLAG_ENFORCE_QUOTA_POLICY_TO_TOP_STARTED_JOBS);
+        // Top-stared jobs are out of quota enforcement.
+        setProcessState(ActivityManager.PROCESS_STATE_TOP);
+        synchronized (mQuotaController.mLock) {
+            trackJobs(jobDefIWF);
+            mQuotaController.prepareForExecutionLocked(jobDefIWF);
+        }
+        setProcessState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND);
+        synchronized (mQuotaController.mLock) {
+            assertEquals(timeUntilQuotaConsumedMs,
+                    mQuotaController.getMaxJobExecutionTimeMsLocked((jobDefIWF)));
+            mQuotaController.maybeStopTrackingJobLocked(jobDefIWF, null);
+        }
+
+        setProcessState(ActivityManager.PROCESS_STATE_RECEIVER);
+        synchronized (mQuotaController.mLock) {
+            assertEquals(timeUntilQuotaConsumedMs,
+                    mQuotaController.getMaxJobExecutionTimeMsLocked(jobDefIWF));
+        }
+
+        mSetFlagsRule.enableFlags(Flags.FLAG_ENFORCE_QUOTA_POLICY_TO_TOP_STARTED_JOBS);
+        // Quota is enforced for top-started job after the process leaves TOP/BTOP state.
+        setProcessState(ActivityManager.PROCESS_STATE_TOP);
+        synchronized (mQuotaController.mLock) {
+            trackJobs(jobDefIWF);
+            mQuotaController.prepareForExecutionLocked(jobDefIWF);
+        }
+        setProcessState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND);
+        synchronized (mQuotaController.mLock) {
+            assertEquals(timeUntilQuotaConsumedMs,
+                    mQuotaController.getMaxJobExecutionTimeMsLocked((jobDefIWF)));
+            mQuotaController.maybeStopTrackingJobLocked(jobDefIWF, null);
+        }
+
+        setProcessState(ActivityManager.PROCESS_STATE_RECEIVER);
+        synchronized (mQuotaController.mLock) {
+            assertEquals(timeUntilQuotaConsumedMs,
+                    mQuotaController.getMaxJobExecutionTimeMsLocked(jobDefIWF));
+        }
+    }
+
+    @Test
     public void testGetMaxJobExecutionTimeLocked_Regular_Active() {
         JobStatus job = createJobStatus("testGetMaxJobExecutionTimeLocked_Regular_Active", 0);
         setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS,
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/restrictions/ThermalStatusRestrictionTest.java b/services/tests/mockingservicestests/src/com/android/server/job/restrictions/ThermalStatusRestrictionTest.java
index c2c67e6..e04aeec 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/restrictions/ThermalStatusRestrictionTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/restrictions/ThermalStatusRestrictionTest.java
@@ -44,10 +44,13 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.os.PowerManager;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
 import android.platform.test.annotations.RequiresFlagsDisabled;
 import android.platform.test.annotations.RequiresFlagsEnabled;
 import android.platform.test.flag.junit.CheckFlagsRule;
 import android.platform.test.flag.junit.DeviceFlagsValueProvider;
+import android.platform.test.flag.junit.SetFlagsRule;
 import android.provider.DeviceConfig;
 import android.util.DebugUtils;
 
@@ -74,6 +77,8 @@
     private static final String SOURCE_PACKAGE = "com.android.frameworks.mockingservicestests";
     private static final int SOURCE_USER_ID = 0;
 
+    @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
     private ThermalStatusRestriction mThermalStatusRestriction;
     private PowerManager.OnThermalStatusChangedListener mStatusChangedListener;
 
@@ -427,6 +432,7 @@
      */
     @Test
     @RequiresFlagsEnabled(FLAG_THERMAL_RESTRICTIONS_TO_FGS_JOBS)
+    @DisableFlags(android.app.job.Flags.FLAG_IGNORE_IMPORTANT_WHILE_FOREGROUND)
     public void testIsJobRestrictedBiasFgs_flagThermalRestrictionsToFgsJobsEnabled() {
         JobStatusContainer jc =
                 new JobStatusContainer("testIsJobRestrictedBiasFgs", mJobSchedulerService);
@@ -508,6 +514,91 @@
         }
     }
 
+    @Test
+    @RequiresFlagsEnabled(FLAG_THERMAL_RESTRICTIONS_TO_FGS_JOBS)
+    @EnableFlags(android.app.job.Flags.FLAG_IGNORE_IMPORTANT_WHILE_FOREGROUND)
+    public void testIsJobRestrictedBiasFgs_flagThermalRestrictionsToFgsJobsEnabled_ignoreIWF() {
+        JobStatusContainer jc =
+                new JobStatusContainer("testIsJobRestrictedBiasFgs", mJobSchedulerService);
+        int jobBias = JobInfo.BIAS_FOREGROUND_SERVICE;
+        for (int thermalStatus : jc.thermalStatuses) {
+            String msg = debugTag(jobBias, thermalStatus);
+            mStatusChangedListener.onThermalStatusChanged(thermalStatus);
+            if (thermalStatus >= THERMAL_STATUS_SEVERE) {
+                // Full restrictions on all jobs
+                assertTrue(msg, isJobRestricted(jc.jobMinPriority, jobBias));
+                assertTrue(msg, isJobRestricted(jc.jobLowPriority, jobBias));
+                assertTrue(msg, isJobRestricted(jc.jobLowPriorityRunning, jobBias));
+                assertTrue(msg, isJobRestricted(jc.jobLowPriorityRunningLong, jobBias));
+                assertTrue(msg, isJobRestricted(jc.jobDefaultPriority, jobBias));
+                assertTrue(msg, isJobRestricted(jc.jobHighPriority, jobBias));
+                assertTrue(msg, isJobRestricted(jc.jobHighPriorityRunning, jobBias));
+                assertTrue(msg, isJobRestricted(jc.jobHighPriorityRunningLong, jobBias));
+                assertTrue(msg, isJobRestricted(jc.ej, jobBias));
+                assertTrue(msg, isJobRestricted(jc.ejDowngraded, jobBias));
+                assertTrue(msg, isJobRestricted(jc.ejRetried, jobBias));
+                assertTrue(msg, isJobRestricted(jc.ejRunning, jobBias));
+                assertTrue(msg, isJobRestricted(jc.ejRunningLong, jobBias));
+                assertTrue(msg, isJobRestricted(jc.ui, jobBias));
+                assertTrue(msg, isJobRestricted(jc.uiRetried, jobBias));
+                assertTrue(msg, isJobRestricted(jc.uiRunning, jobBias));
+                assertTrue(msg, isJobRestricted(jc.uiRunningLong, jobBias));
+            } else if (thermalStatus >= THERMAL_STATUS_MODERATE) {
+                // No restrictions on user related jobs
+                assertFalse(msg, isJobRestricted(jc.ui, jobBias));
+                assertFalse(msg, isJobRestricted(jc.uiRetried, jobBias));
+                assertFalse(msg, isJobRestricted(jc.uiRunning, jobBias));
+                assertFalse(msg, isJobRestricted(jc.uiRunningLong, jobBias));
+                // Some restrictions on expedited jobs
+                assertFalse(msg, isJobRestricted(jc.ej, jobBias));
+                assertTrue(msg, isJobRestricted(jc.ejDowngraded, jobBias));
+                assertTrue(msg, isJobRestricted(jc.ejRetried, jobBias));
+                assertFalse(msg, isJobRestricted(jc.ejRunning, jobBias));
+                assertTrue(msg, isJobRestricted(jc.ejRunningLong, jobBias));
+                // Some restrictions on high priority jobs
+                assertTrue(msg, isJobRestricted(jc.jobHighPriority, jobBias));
+                assertFalse(msg, isJobRestricted(jc.jobHighPriorityRunning, jobBias));
+                assertTrue(msg, isJobRestricted(jc.jobHighPriorityRunningLong, jobBias));
+                // Full restructions on important while foreground jobs as
+                // the important while foreground flag is ignored.
+                assertTrue(isJobRestricted(jc.importantWhileForeground, jobBias));
+                assertTrue(isJobRestricted(jc.importantWhileForegroundRunning, jobBias));
+                assertTrue(isJobRestricted(jc.importantWhileForegroundRunningLong, jobBias));
+                // Full restriction on default priority jobs
+                assertTrue(msg, isJobRestricted(jc.jobDefaultPriority, jobBias));
+                // Full restriction on low priority jobs
+                assertTrue(msg, isJobRestricted(jc.jobLowPriority, jobBias));
+                assertTrue(msg, isJobRestricted(jc.jobLowPriorityRunning, jobBias));
+                assertTrue(msg, isJobRestricted(jc.jobLowPriorityRunningLong, jobBias));
+                // Full restriction on min priority jobs
+                assertTrue(msg, isJobRestricted(jc.jobMinPriority, jobBias));
+            } else {
+                // thermalStatus < THERMAL_STATUS_MODERATE
+                // No restrictions on any job type
+                assertFalse(msg, isJobRestricted(jc.ui, jobBias));
+                assertFalse(msg, isJobRestricted(jc.uiRetried, jobBias));
+                assertFalse(msg, isJobRestricted(jc.uiRunning, jobBias));
+                assertFalse(msg, isJobRestricted(jc.uiRunningLong, jobBias));
+                assertFalse(msg, isJobRestricted(jc.ej, jobBias));
+                assertFalse(msg, isJobRestricted(jc.ejDowngraded, jobBias));
+                assertFalse(msg, isJobRestricted(jc.ejRetried, jobBias));
+                assertFalse(msg, isJobRestricted(jc.ejRunning, jobBias));
+                assertFalse(msg, isJobRestricted(jc.ejRunningLong, jobBias));
+                assertFalse(msg, isJobRestricted(jc.jobHighPriority, jobBias));
+                assertFalse(msg, isJobRestricted(jc.jobHighPriorityRunning, jobBias));
+                assertFalse(msg, isJobRestricted(jc.jobHighPriorityRunningLong, jobBias));
+                assertFalse(msg, isJobRestricted(jc.importantWhileForeground, jobBias));
+                assertFalse(msg, isJobRestricted(jc.importantWhileForegroundRunning, jobBias));
+                assertFalse(msg, isJobRestricted(jc.importantWhileForegroundRunningLong, jobBias));
+                assertFalse(msg, isJobRestricted(jc.jobDefaultPriority, jobBias));
+                assertFalse(msg, isJobRestricted(jc.jobLowPriority, jobBias));
+                assertFalse(msg, isJobRestricted(jc.jobLowPriorityRunning, jobBias));
+                assertFalse(msg, isJobRestricted(jc.jobLowPriorityRunningLong, jobBias));
+                assertFalse(msg, isJobRestricted(jc.jobMinPriority, jobBias));
+            }
+        }
+    }
+
     /**
      * Test {@link JobSchedulerService#isJobRestricted(JobStatus)} when Job Bias is less than
      * Foreground Service and all Thermal states.
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/BackgroundUserSoundNotifierTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/BackgroundUserSoundNotifierTest.java
index bf946a1..3d0c637 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/BackgroundUserSoundNotifierTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/BackgroundUserSoundNotifierTest.java
@@ -103,6 +103,7 @@
         assumeTrue(UserManager.supportsMultipleUsers());
         AudioAttributes aa = new AudioAttributes.Builder().setUsage(USAGE_ALARM).build();
         UserInfo user = createUser("User", UserManager.USER_TYPE_FULL_SECONDARY, 0);
+
         final int fgUserId = mSpiedContext.getUserId();
         final int bgUserUid = user.id * 100000;
         doReturn(UserHandle.of(fgUserId)).when(mSpiedContext).getUser();
@@ -209,6 +210,28 @@
                         eq(UserHandle.of(fgUserId)));
     }
 
+    @Test
+    public void testOnAudioFocusGrant_alarmOnProfileOfForegroundUser_foregroundUserNotNotified() {
+        assumeTrue(UserManager.supportsMultipleUsers());
+        final int fgUserId = mSpiedContext.getUserId();
+        UserInfo fgUserProfile = createProfileForUser("Background profile",
+                UserManager.USER_TYPE_PROFILE_MANAGED, fgUserId, null);
+        assumeTrue("Cannot add a profile", fgUserProfile != null);
+        int fgUserProfileUid = fgUserProfile.id * 100_000;
+
+        AudioAttributes aa = new AudioAttributes.Builder().setUsage(USAGE_ALARM).build();
+        AudioFocusInfo afi = new AudioFocusInfo(aa, fgUserProfileUid, "", "",
+                AudioManager.AUDIOFOCUS_GAIN, 0, 0, Build.VERSION.SDK_INT);
+
+        mBackgroundUserSoundNotifier.getAudioPolicyFocusListener()
+                .onAudioFocusGrant(afi, AudioManager.AUDIOFOCUS_REQUEST_GRANTED);
+
+        verify(mNotificationManager, never())
+                .notifyAsUser(eq(BackgroundUserSoundNotifier.class.getSimpleName()),
+                        eq(afi.getClientUid()), any(Notification.class),
+                        eq(UserHandle.of(fgUserId)));
+    }
+
 
     @Test
     public void testCreateNotification_UserSwitcherEnabled_bothActionsAvailable() {
@@ -327,6 +350,17 @@
         }
         return user;
     }
+
+    private UserInfo createProfileForUser(String name, String userType, int userHandle,
+            String[] disallowedPackages) {
+        UserInfo profile = mUserManager.createProfileForUser(
+                name, userType, 0, userHandle, disallowedPackages);
+        if (profile != null) {
+            mUsersToRemove.add(profile.id);
+        }
+        return profile;
+    }
+
     private void removeUser(int userId) {
         mUserManager.removeUser(userId);
     }
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/StagingManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/StagingManagerTest.java
index 124c41e..591e8df 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/StagingManagerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/StagingManagerTest.java
@@ -742,7 +742,10 @@
                 /* stagedSessionErrorMessage */ "no error",
                 /* preVerifiedDomains */ null,
                 /* verifierController */ null,
-                /* verificationPolicy */ PackageInstaller.VERIFICATION_POLICY_BLOCK_FAIL_CLOSED);
+                /* initialVerificationPolicy */ 
+                PackageInstaller.VERIFICATION_POLICY_BLOCK_FAIL_CLOSED,
+                /* currentVerificationPolicy */
+                PackageInstaller.VERIFICATION_POLICY_BLOCK_FAIL_CLOSED);
 
         StagingManager.StagedSession stagedSession = spy(session.mStagedSession);
         doReturn(packageName).when(stagedSession).getPackageName();
diff --git a/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java b/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java
index c099517..1ea3674 100644
--- a/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java
@@ -40,7 +40,6 @@
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
 import static org.junit.Assume.assumeThat;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
@@ -55,6 +54,7 @@
 import android.app.Flags;
 import android.app.WallpaperColors;
 import android.app.WallpaperManager;
+import android.app.wallpaper.WallpaperDescription;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -65,6 +65,7 @@
 import android.graphics.Color;
 import android.hardware.display.DisplayManager;
 import android.os.ParcelFileDescriptor;
+import android.os.PersistableBundle;
 import android.os.RemoteException;
 import android.os.SystemClock;
 import android.platform.test.annotations.DisableFlags;
@@ -426,7 +427,8 @@
 
     @Test
     @EnableFlags(Flags.FLAG_REMOVE_NEXT_WALLPAPER_COMPONENT)
-    public void testSaveLoadSettings() {
+    public void testSaveLoadSettings_withoutWallpaperDescription()
+            throws IOException, XmlPullParserException {
         WallpaperData expectedData = mService.getCurrentWallpaperData(FLAG_SYSTEM, 0);
         expectedData.setComponent(sDefaultWallpaperComponent);
         expectedData.primaryColors = new WallpaperColors(Color.valueOf(Color.RED),
@@ -436,27 +438,19 @@
         expectedData.mUidToDimAmount.put(1, 0.4f);
 
         ByteArrayOutputStream ostream = new ByteArrayOutputStream();
-        try {
-            TypedXmlSerializer serializer = Xml.newBinarySerializer();
-            serializer.setOutput(ostream, StandardCharsets.UTF_8.name());
-            mService.mWallpaperDataParser.saveSettingsToSerializer(serializer, expectedData, null);
-            ostream.close();
-        } catch (IOException e) {
-            fail("exception occurred while writing system wallpaper attributes");
-        }
+        TypedXmlSerializer serializer = Xml.newBinarySerializer();
+        serializer.setOutput(ostream, StandardCharsets.UTF_8.name());
+        mService.mWallpaperDataParser.saveSettingsToSerializer(serializer, expectedData, null);
+        ostream.close();
 
         WallpaperData actualData = new WallpaperData(0, FLAG_SYSTEM);
-        try {
-            ByteArrayInputStream istream = new ByteArrayInputStream(ostream.toByteArray());
-            TypedXmlPullParser parser = Xml.newBinaryPullParser();
-            parser.setInput(istream, StandardCharsets.UTF_8.name());
-            mService.mWallpaperDataParser.loadSettingsFromSerializer(parser,
-                    actualData, /* userId= */0, /* loadSystem= */ true, /* loadLock= */
-                    false, /* keepDimensionHints= */ true,
-                    new WallpaperDisplayHelper.DisplayData(0));
-        } catch (IOException | XmlPullParserException e) {
-            fail("exception occurred while parsing wallpaper");
-        }
+        ByteArrayInputStream istream = new ByteArrayInputStream(ostream.toByteArray());
+        TypedXmlPullParser parser = Xml.newBinaryPullParser();
+        parser.setInput(istream, StandardCharsets.UTF_8.name());
+        mService.mWallpaperDataParser.loadSettingsFromSerializer(parser,
+                actualData, /* userId= */0, /* loadSystem= */ true, /* loadLock= */
+                false, /* keepDimensionHints= */ true,
+                new WallpaperDisplayHelper.DisplayData(0));
 
         assertThat(actualData.getComponent()).isEqualTo(expectedData.getComponent());
         assertThat(actualData.primaryColors).isEqualTo(expectedData.primaryColors);
@@ -472,33 +466,58 @@
     }
 
     @Test
+    @EnableFlags(Flags.FLAG_REMOVE_NEXT_WALLPAPER_COMPONENT)
+    public void testSaveLoadSettings_withWallpaperDescription()
+            throws IOException, XmlPullParserException {
+        WallpaperData expectedData = mService.getCurrentWallpaperData(FLAG_SYSTEM, 0);
+        expectedData.setComponent(sDefaultWallpaperComponent);
+        PersistableBundle content = new PersistableBundle();
+        content.putString("ckey", "cvalue");
+        WallpaperDescription description = new WallpaperDescription.Builder()
+                .setComponent(sDefaultWallpaperComponent).setId("testId").setTitle("fake one")
+                .setContent(content).build();
+        expectedData.setDescription(description);
+
+        ByteArrayOutputStream ostream = new ByteArrayOutputStream();
+        TypedXmlSerializer serializer = Xml.newBinarySerializer();
+        serializer.setOutput(ostream, StandardCharsets.UTF_8.name());
+        mService.mWallpaperDataParser.saveSettingsToSerializer(serializer, expectedData, null);
+        ostream.close();
+
+        WallpaperData actualData = new WallpaperData(0, FLAG_SYSTEM);
+        ByteArrayInputStream istream = new ByteArrayInputStream(ostream.toByteArray());
+        TypedXmlPullParser parser = Xml.newBinaryPullParser();
+        parser.setInput(istream, StandardCharsets.UTF_8.name());
+        mService.mWallpaperDataParser.loadSettingsFromSerializer(parser,
+                actualData, /* userId= */0, /* loadSystem= */ true, /* loadLock= */
+                false, /* keepDimensionHints= */ true,
+                new WallpaperDisplayHelper.DisplayData(0));
+
+        assertThat(actualData.getComponent()).isEqualTo(expectedData.getComponent());
+        assertThat(actualData.getDescription()).isEqualTo(expectedData.getDescription());
+    }
+
+    @Test
     @DisableFlags(Flags.FLAG_REMOVE_NEXT_WALLPAPER_COMPONENT)
-    public void testSaveLoadSettings_legacyNextComponent() {
+    public void testSaveLoadSettings_legacyNextComponent()
+            throws IOException, XmlPullParserException {
         WallpaperData systemWallpaperData = mService.getCurrentWallpaperData(FLAG_SYSTEM, 0);
         systemWallpaperData.setComponent(sDefaultWallpaperComponent);
         ByteArrayOutputStream ostream = new ByteArrayOutputStream();
-        try {
-            TypedXmlSerializer serializer = Xml.newBinarySerializer();
-            serializer.setOutput(ostream, StandardCharsets.UTF_8.name());
-            mService.mWallpaperDataParser.saveSettingsToSerializer(serializer, systemWallpaperData,
-                    null);
-            ostream.close();
-        } catch (IOException e) {
-            fail("exception occurred while writing system wallpaper attributes");
-        }
+        TypedXmlSerializer serializer = Xml.newBinarySerializer();
+        serializer.setOutput(ostream, StandardCharsets.UTF_8.name());
+        mService.mWallpaperDataParser.saveSettingsToSerializer(serializer, systemWallpaperData,
+                null);
+        ostream.close();
 
         WallpaperData shouldMatchSystem = new WallpaperData(0, FLAG_SYSTEM);
-        try {
-            ByteArrayInputStream istream = new ByteArrayInputStream(ostream.toByteArray());
-            TypedXmlPullParser parser = Xml.newBinaryPullParser();
-            parser.setInput(istream, StandardCharsets.UTF_8.name());
-            mService.mWallpaperDataParser.loadSettingsFromSerializer(parser,
-                    shouldMatchSystem, /* userId= */0, /* loadSystem= */ true, /* loadLock= */
-                    false, /* keepDimensionHints= */ true,
-                    new WallpaperDisplayHelper.DisplayData(0));
-        } catch (IOException | XmlPullParserException e) {
-            fail("exception occurred while parsing wallpaper");
-        }
+        ByteArrayInputStream istream = new ByteArrayInputStream(ostream.toByteArray());
+        TypedXmlPullParser parser = Xml.newBinaryPullParser();
+        parser.setInput(istream, StandardCharsets.UTF_8.name());
+        mService.mWallpaperDataParser.loadSettingsFromSerializer(parser,
+                shouldMatchSystem, /* userId= */0, /* loadSystem= */ true, /* loadLock= */
+                false, /* keepDimensionHints= */ true,
+                new WallpaperDisplayHelper.DisplayData(0));
 
         assertThat(shouldMatchSystem.nextWallpaperComponent).isEqualTo(
                 systemWallpaperData.getComponent());
diff --git a/services/tests/security/forensic/Android.bp b/services/tests/security/forensic/Android.bp
index adc4904..77a87af 100644
--- a/services/tests/security/forensic/Android.bp
+++ b/services/tests/security/forensic/Android.bp
@@ -24,6 +24,9 @@
         "platform-test-annotations",
         "services.core",
         "truth",
+        "Nene",
+        "Harrier",
+        "TestApp",
     ],
 
     platform_apis: true,
diff --git a/services/tests/security/forensic/AndroidManifest.xml b/services/tests/security/forensic/AndroidManifest.xml
index 40feb19..c5b3d40 100644
--- a/services/tests/security/forensic/AndroidManifest.xml
+++ b/services/tests/security/forensic/AndroidManifest.xml
@@ -15,10 +15,13 @@
 -->
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-     package="com.android.server.security.forensic.tests">
+  package="com.android.server.security.forensic.tests">
+
+       <uses-permission android:name="android.permission.MANAGE_DEVICE_POLICY_AUDIT_LOGGING"     />
+       <uses-permission android:name="android.permission.MANAGE_DEVICE_ADMINS" />
 
     <application android:testOnly="true">
-        <uses-library android:name="android.test.runner"/>
+      <uses-library android:name="android.test.runner"/>
     </application>
 
     <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
diff --git a/services/tests/security/forensic/src/com/android/server/security/forensic/ForensicServiceTest.java b/services/tests/security/forensic/src/com/android/server/security/forensic/ForensicServiceTest.java
index 2b55303..40e0034 100644
--- a/services/tests/security/forensic/src/com/android/server/security/forensic/ForensicServiceTest.java
+++ b/services/tests/security/forensic/src/com/android/server/security/forensic/ForensicServiceTest.java
@@ -19,22 +19,34 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
+import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
 
 import android.annotation.SuppressLint;
 import android.content.Context;
 import android.os.Looper;
 import android.os.RemoteException;
 import android.os.test.TestLooper;
+import android.security.forensic.ForensicEvent;
 import android.security.forensic.IForensicServiceCommandCallback;
 import android.security.forensic.IForensicServiceStateCallback;
+import android.util.ArrayMap;
+
+import androidx.test.core.app.ApplicationProvider;
+
+import com.android.server.ServiceThread;
 
 import org.junit.Before;
 import org.junit.Test;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
+import org.mockito.ArgumentCaptor;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
 
 public class ForensicServiceTest {
     private static final int STATE_UNKNOWN = IForensicServiceStateCallback.State.UNKNOWN;
@@ -52,21 +64,24 @@
     private static final int ERROR_DATA_SOURCE_UNAVAILABLE =
             IForensicServiceCommandCallback.ErrorCode.DATA_SOURCE_UNAVAILABLE;
 
-    @Mock
     private Context mContext;
     private BackupTransportConnection mBackupTransportConnection;
-
+    private DataAggregator mDataAggregator;
     private ForensicService mForensicService;
     private TestLooper mTestLooper;
     private Looper mLooper;
+    private TestLooper mTestLooperOfDataAggregator;
+    private Looper mLooperOfDataAggregator;
 
     @SuppressLint("VisibleForTests")
     @Before
     public void setUp() {
-        MockitoAnnotations.initMocks(this);
+        mContext = spy(ApplicationProvider.getApplicationContext());
 
         mTestLooper = new TestLooper();
         mLooper = mTestLooper.getLooper();
+        mTestLooperOfDataAggregator = new TestLooper();
+        mLooperOfDataAggregator = mTestLooperOfDataAggregator.getLooper();
         mForensicService = new ForensicService(new MockInjector(mContext));
         mForensicService.onStart();
     }
@@ -121,6 +136,8 @@
         assertEquals(STATE_INVISIBLE, scb1.mState);
         assertEquals(STATE_INVISIBLE, scb2.mState);
 
+        doReturn(true).when(mDataAggregator).initialize();
+
         CommandCallback ccb = new CommandCallback();
         mForensicService.getBinderService().makeVisible(ccb);
         mTestLooper.dispatchAll();
@@ -130,6 +147,29 @@
     }
 
     @Test
+    public void testMakeVisible_FromInvisible_TwoMonitors_DataSourceUnavailable()
+            throws RemoteException {
+        mForensicService.setState(STATE_INVISIBLE);
+        StateCallback scb1 = new StateCallback();
+        StateCallback scb2 = new StateCallback();
+        mForensicService.getBinderService().monitorState(scb1);
+        mForensicService.getBinderService().monitorState(scb2);
+        mTestLooper.dispatchAll();
+        assertEquals(STATE_INVISIBLE, scb1.mState);
+        assertEquals(STATE_INVISIBLE, scb2.mState);
+
+        doReturn(false).when(mDataAggregator).initialize();
+
+        CommandCallback ccb = new CommandCallback();
+        mForensicService.getBinderService().makeVisible(ccb);
+        mTestLooper.dispatchAll();
+        assertEquals(STATE_INVISIBLE, scb1.mState);
+        assertEquals(STATE_INVISIBLE, scb2.mState);
+        assertNotNull(ccb.mErrorCode);
+        assertEquals(ERROR_DATA_SOURCE_UNAVAILABLE, ccb.mErrorCode.intValue());
+    }
+
+    @Test
     public void testMakeVisible_FromVisible_TwoMonitors() throws RemoteException {
         mForensicService.setState(STATE_VISIBLE);
         StateCallback scb1 = new StateCallback();
@@ -262,6 +302,8 @@
         CommandCallback ccb = new CommandCallback();
         mForensicService.getBinderService().enable(ccb);
         mTestLooper.dispatchAll();
+
+        verify(mDataAggregator, times(1)).enable();
         assertEquals(STATE_ENABLED, scb1.mState);
         assertEquals(STATE_ENABLED, scb2.mState);
         assertNull(ccb.mErrorCode);
@@ -361,14 +403,67 @@
 
         doNothing().when(mBackupTransportConnection).release();
 
+        ServiceThread mockThread = spy(ServiceThread.class);
+        mDataAggregator.setHandler(mLooperOfDataAggregator, mockThread);
+
         CommandCallback ccb = new CommandCallback();
         mForensicService.getBinderService().disable(ccb);
         mTestLooper.dispatchAll();
+        mTestLooperOfDataAggregator.dispatchAll();
+        // TODO: We can verify the data sources once we implement them.
+        verify(mockThread, times(1)).quitSafely();
         assertEquals(STATE_VISIBLE, scb1.mState);
         assertEquals(STATE_VISIBLE, scb2.mState);
         assertNull(ccb.mErrorCode);
     }
 
+    @Test
+    public void testDataAggregator_AddBatchData() {
+        mForensicService.setState(STATE_ENABLED);
+        ServiceThread mockThread = spy(ServiceThread.class);
+        mDataAggregator.setHandler(mLooperOfDataAggregator, mockThread);
+
+        String eventOneType = "event_one_type";
+        String eventOneMapKey = "event_one_map_key";
+        String eventOneMapVal = "event_one_map_val";
+        Map<String, String> eventOneMap = new ArrayMap<String, String>();
+        eventOneMap.put(eventOneMapKey, eventOneMapVal);
+        ForensicEvent eventOne = new ForensicEvent(eventOneType, eventOneMap);
+
+        String eventTwoType = "event_two_type";
+        String eventTwoMapKey = "event_two_map_key";
+        String eventTwoMapVal = "event_two_map_val";
+        Map<String, String> eventTwoMap = new ArrayMap<String, String>();
+        eventTwoMap.put(eventTwoMapKey, eventTwoMapVal);
+        ForensicEvent eventTwo = new ForensicEvent(eventTwoType, eventTwoMap);
+
+        List<ForensicEvent> events = new ArrayList<>();
+        events.add(eventOne);
+        events.add(eventTwo);
+
+        doReturn(true).when(mBackupTransportConnection).addData(any());
+
+        mDataAggregator.addBatchData(events);
+        mTestLooperOfDataAggregator.dispatchAll();
+        mTestLooper.dispatchAll();
+
+        ArgumentCaptor<List<ForensicEvent>> captor = ArgumentCaptor.forClass(List.class);
+        verify(mBackupTransportConnection).addData(captor.capture());
+        List<ForensicEvent> receivedEvents = captor.getValue();
+        assertEquals(receivedEvents.size(), 2);
+
+        assertEquals(receivedEvents.getFirst().getType(), eventOneType);
+        assertEquals(receivedEvents.getFirst().getKeyValuePairs().size(), 1);
+        assertEquals(receivedEvents.getFirst().getKeyValuePairs().get(eventOneMapKey),
+                eventOneMapVal);
+
+        assertEquals(receivedEvents.getLast().getType(), eventTwoType);
+        assertEquals(receivedEvents.getLast().getKeyValuePairs().size(), 1);
+        assertEquals(receivedEvents.getLast().getKeyValuePairs().get(eventTwoMapKey),
+                eventTwoMapVal);
+
+    }
+
     private class MockInjector implements ForensicService.Injector {
         private final Context mContext;
 
@@ -393,6 +488,11 @@
             return mBackupTransportConnection;
         }
 
+        @Override
+        public DataAggregator getDataAggregator(ForensicService forensicService) {
+            mDataAggregator = spy(new DataAggregator(mContext, forensicService));
+            return mDataAggregator;
+        }
     }
 
     private static class StateCallback extends IForensicServiceStateCallback.Stub {
diff --git a/services/tests/servicestests/src/com/android/server/audio/FadeOutManagerTest.java b/services/tests/servicestests/src/com/android/server/audio/FadeOutManagerTest.java
index fa94821..3c27af4 100644
--- a/services/tests/servicestests/src/com/android/server/audio/FadeOutManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/audio/FadeOutManagerTest.java
@@ -34,6 +34,8 @@
 
 import com.google.common.truth.Expect;
 
+import com.android.server.utils.EventLogger;
+
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -67,6 +69,8 @@
     PlayerBase.PlayerIdCard mMockPlayerIdCard;
     @Mock
     AudioPlaybackConfiguration mMockPlaybackConfiguration;
+    @Mock
+    EventLogger mMockEventLogger;
 
     @Rule
     public final Expect expect = Expect.create();
@@ -193,7 +197,7 @@
             String packageName, int uid, int flags) {
         MediaFocusControl mfc = new MediaFocusControl(mContext, null);
         return new FocusRequester(aa, AudioManager.AUDIOFOCUS_GAIN, flags, null, null, clientId,
-                null, packageName, uid, mfc, 1);
+                null, packageName, uid, mfc, 1, mMockEventLogger);
     }
 
     private PlayerBase.PlayerIdCard createPlayerIdCard(AudioAttributes aa, int playerType) {
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java b/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java
index b4b3612..bc410d9 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java
@@ -1580,12 +1580,12 @@
         setupAuthForOnly(TYPE_FINGERPRINT, Authenticators.BIOMETRIC_STRONG);
 
         assertEquals(BiometricManager.BIOMETRIC_SUCCESS,
-                invokeCanAuthenticate(mBiometricService, Authenticators.MANDATORY_BIOMETRICS));
+                invokeCanAuthenticate(mBiometricService, Authenticators.IDENTITY_CHECK));
 
         when(mTrustManager.isInSignificantPlace()).thenReturn(true);
 
-        assertEquals(BiometricManager.BIOMETRIC_ERROR_MANDATORY_NOT_ACTIVE,
-                invokeCanAuthenticate(mBiometricService, Authenticators.MANDATORY_BIOMETRICS));
+        assertEquals(BiometricManager.BIOMETRIC_ERROR_IDENTITY_CHECK_NOT_ACTIVE,
+                invokeCanAuthenticate(mBiometricService, Authenticators.IDENTITY_CHECK));
     }
 
     @Test
@@ -1603,13 +1603,13 @@
         setupAuthForOnly(TYPE_FINGERPRINT, Authenticators.BIOMETRIC_STRONG);
 
         assertEquals(BiometricManager.BIOMETRIC_SUCCESS,
-                invokeCanAuthenticate(mBiometricService, Authenticators.MANDATORY_BIOMETRICS
+                invokeCanAuthenticate(mBiometricService, Authenticators.IDENTITY_CHECK
                         | Authenticators.BIOMETRIC_STRONG));
 
         when(mTrustManager.isInSignificantPlace()).thenReturn(true);
 
         assertEquals(BiometricManager.BIOMETRIC_SUCCESS,
-                invokeCanAuthenticate(mBiometricService, Authenticators.MANDATORY_BIOMETRICS
+                invokeCanAuthenticate(mBiometricService, Authenticators.IDENTITY_CHECK
                         | Authenticators.BIOMETRIC_STRONG));
     }
 
@@ -1628,12 +1628,12 @@
         setupAuthForOnly(TYPE_CREDENTIAL, Authenticators.DEVICE_CREDENTIAL);
 
         assertEquals(BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE,
-                invokeCanAuthenticate(mBiometricService, Authenticators.MANDATORY_BIOMETRICS));
+                invokeCanAuthenticate(mBiometricService, Authenticators.IDENTITY_CHECK));
 
         when(mTrustManager.isInSignificantPlace()).thenReturn(true);
 
         assertEquals(BiometricManager.BIOMETRIC_SUCCESS,
-                invokeCanAuthenticate(mBiometricService, Authenticators.MANDATORY_BIOMETRICS
+                invokeCanAuthenticate(mBiometricService, Authenticators.IDENTITY_CHECK
                         | Authenticators.DEVICE_CREDENTIAL));
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/PreAuthInfoTest.java b/services/tests/servicestests/src/com/android/server/biometrics/PreAuthInfoTest.java
index b758f57..85e45f4 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/PreAuthInfoTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/PreAuthInfoTest.java
@@ -207,7 +207,7 @@
 
         final BiometricSensor sensor = getFaceSensor();
         final PromptInfo promptInfo = new PromptInfo();
-        promptInfo.setAuthenticators(BiometricManager.Authenticators.MANDATORY_BIOMETRICS);
+        promptInfo.setAuthenticators(BiometricManager.Authenticators.IDENTITY_CHECK);
         final PreAuthInfo preAuthInfo = PreAuthInfo.create(mTrustManager, mDevicePolicyManager,
                 mSettingObserver, List.of(sensor), 0 /* userId */, promptInfo, TEST_PACKAGE_NAME,
                 false /* checkDevicePolicyManager */, mContext, mBiometricCameraManager);
@@ -222,7 +222,7 @@
         when(mTrustManager.isInSignificantPlace()).thenReturn(false);
 
         final PromptInfo promptInfo = new PromptInfo();
-        promptInfo.setAuthenticators(BiometricManager.Authenticators.MANDATORY_BIOMETRICS);
+        promptInfo.setAuthenticators(BiometricManager.Authenticators.IDENTITY_CHECK);
         final PreAuthInfo preAuthInfo = PreAuthInfo.create(mTrustManager, mDevicePolicyManager,
                 mSettingObserver, List.of(), 0 /* userId */, promptInfo, TEST_PACKAGE_NAME,
                 false /* checkDevicePolicyManager */, mContext, mBiometricCameraManager);
@@ -238,7 +238,7 @@
 
         final BiometricSensor sensor = getFaceSensor();
         final PromptInfo promptInfo = new PromptInfo();
-        promptInfo.setAuthenticators(BiometricManager.Authenticators.MANDATORY_BIOMETRICS
+        promptInfo.setAuthenticators(BiometricManager.Authenticators.IDENTITY_CHECK
                 | BiometricManager.Authenticators.BIOMETRIC_STRONG);
         final PreAuthInfo preAuthInfo = PreAuthInfo.create(mTrustManager, mDevicePolicyManager,
                 mSettingObserver, List.of(sensor), 0 /* userId */, promptInfo, TEST_PACKAGE_NAME,
@@ -255,13 +255,13 @@
 
         final BiometricSensor sensor = getFaceSensor();
         final PromptInfo promptInfo = new PromptInfo();
-        promptInfo.setAuthenticators(BiometricManager.Authenticators.MANDATORY_BIOMETRICS);
+        promptInfo.setAuthenticators(BiometricManager.Authenticators.IDENTITY_CHECK);
         final PreAuthInfo preAuthInfo = PreAuthInfo.create(mTrustManager, mDevicePolicyManager,
                 mSettingObserver, List.of(sensor), 0 /* userId */, promptInfo, TEST_PACKAGE_NAME,
                 false /* checkDevicePolicyManager */, mContext, mBiometricCameraManager);
 
         assertThat(preAuthInfo.getCanAuthenticateResult()).isEqualTo(
-                BiometricManager.BIOMETRIC_ERROR_MANDATORY_NOT_ACTIVE);
+                BiometricManager.BIOMETRIC_ERROR_IDENTITY_CHECK_NOT_ACTIVE);
         assertThat(preAuthInfo.eligibleSensors).hasSize(0);
     }
 
@@ -296,7 +296,7 @@
 
         final BiometricSensor sensor = getFaceSensor();
         final PromptInfo promptInfo = new PromptInfo();
-        promptInfo.setAuthenticators(BiometricManager.Authenticators.MANDATORY_BIOMETRICS);
+        promptInfo.setAuthenticators(BiometricManager.Authenticators.IDENTITY_CHECK);
         promptInfo.setNegativeButtonText(TEST_PACKAGE_NAME);
         final PreAuthInfo preAuthInfo = PreAuthInfo.create(mTrustManager, mDevicePolicyManager,
                 mSettingObserver, List.of(sensor), 0 /* userId */, promptInfo, TEST_PACKAGE_NAME,
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/UtilsTest.java b/services/tests/servicestests/src/com/android/server/biometrics/UtilsTest.java
index 1bea371..c4167d2 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/UtilsTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/UtilsTest.java
@@ -212,24 +212,24 @@
                 mContext, Authenticators.BIOMETRIC_MIN_STRENGTH));
 
         assertThrows(SecurityException.class, () -> Utils.isValidAuthenticatorConfig(
-                        mContext, Authenticators.MANDATORY_BIOMETRICS));
+                        mContext, Authenticators.IDENTITY_CHECK));
 
         doNothing().when(mContext).enforceCallingOrSelfPermission(
                 eq(SET_BIOMETRIC_DIALOG_ADVANCED), any());
 
         if (Flags.mandatoryBiometrics()) {
             assertTrue(Utils.isValidAuthenticatorConfig(mContext,
-                    Authenticators.MANDATORY_BIOMETRICS));
+                    Authenticators.IDENTITY_CHECK));
         } else {
             assertFalse(Utils.isValidAuthenticatorConfig(mContext,
-                    Authenticators.MANDATORY_BIOMETRICS));
+                    Authenticators.IDENTITY_CHECK));
         }
 
         // The rest of the bits are not allowed to integrate with the public APIs
         for (int i = 8; i < 32; i++) {
             final int authenticator = 1 << i;
             if (authenticator == Authenticators.DEVICE_CREDENTIAL
-                    || authenticator == Authenticators.MANDATORY_BIOMETRICS) {
+                    || authenticator == Authenticators.IDENTITY_CHECK) {
                 continue;
             }
             assertFalse(Utils.isValidAuthenticatorConfig(mContext, 1 << i));
@@ -307,8 +307,8 @@
                         BiometricManager.BIOMETRIC_ERROR_LOCKOUT},
                 {BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT,
                         BiometricManager.BIOMETRIC_ERROR_LOCKOUT},
-                {BiometricConstants.BIOMETRIC_ERROR_MANDATORY_NOT_ACTIVE,
-                        BiometricManager.BIOMETRIC_ERROR_MANDATORY_NOT_ACTIVE}
+                {BiometricConstants.BIOMETRIC_ERROR_IDENTITY_CHECK_NOT_ACTIVE,
+                        BiometricManager.BIOMETRIC_ERROR_IDENTITY_CHECK_NOT_ACTIVE}
         };
 
         for (int i = 0; i < testCases.length; i++) {
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/log/BiometricLoggerTest.java b/services/tests/servicestests/src/com/android/server/biometrics/log/BiometricLoggerTest.java
index 8f23ab9..d7bfea8 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/log/BiometricLoggerTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/log/BiometricLoggerTest.java
@@ -16,6 +16,8 @@
 
 package com.android.server.biometrics.log;
 
+import static com.google.common.truth.Truth.assertThat;
+
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.anyBoolean;
 import static org.mockito.Mockito.anyFloat;
@@ -45,10 +47,14 @@
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
+import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 import org.mockito.junit.MockitoJUnit;
 import org.mockito.junit.MockitoRule;
 
+import java.util.Arrays;
+import java.util.stream.Collectors;
+
 @Presubmit
 @SmallTest
 public class BiometricLoggerTest {
@@ -136,12 +142,53 @@
         final int targetUserId = 4;
         final long latency = 44;
         final boolean enrollSuccessful = true;
+        final int templateId = 4;
 
-        mLogger.logOnEnrolled(targetUserId, latency, enrollSuccessful, -1);
+        mLogger.logOnEnrolled(targetUserId, latency, enrollSuccessful, -1, templateId);
 
         verify(mSink).enroll(
                 eq(DEFAULT_MODALITY), eq(DEFAULT_ACTION), eq(DEFAULT_CLIENT),
-                eq(targetUserId), eq(latency), eq(enrollSuccessful), anyFloat(), anyInt());
+                eq(targetUserId), eq(latency), eq(enrollSuccessful), anyFloat(), anyInt(),
+                eq(templateId));
+    }
+
+    @Test
+    public void testUnEnroll() {
+        mLogger = createLogger();
+
+        final int targetUserId = 4;
+        final int reason = BiometricsProtoEnums.UNENROLL_REASON_DANGLING_FRAMEWORK;
+        final int templateId = 4;
+
+        mLogger.logOnUnEnrolled(targetUserId, reason, templateId);
+
+        verify(mSink).unenrolled(
+                eq(DEFAULT_MODALITY), eq(targetUserId), eq(reason), eq(templateId));
+    }
+
+    @Test
+    public void testEnumeration() {
+        mLogger = createLogger();
+
+        final int targetUserId = 4;
+        final int result = BiometricsProtoEnums.ENUMERATION_RESULT_OK;
+        final int[] templateIdsHal = {1, 2};
+        final int[] templateIdsFw = {2, 3};
+        mLogger.logOnEnumerated(targetUserId, result, templateIdsHal, templateIdsFw);
+
+        final ArgumentCaptor<int[]> captorHalIds = ArgumentCaptor.forClass(int[].class);
+        final ArgumentCaptor<int[]> captorFwIds = ArgumentCaptor.forClass(int[].class);
+        verify(mSink).enumerated(
+                eq(DEFAULT_MODALITY), eq(targetUserId), eq(result), captorHalIds.capture(),
+                captorFwIds.capture());
+        assertThat(captorHalIds.getAllValues().stream()
+                .flatMap(x -> Arrays.stream(x).boxed())
+                .collect(Collectors.toList()))
+                .containsExactly(1, 2);
+        assertThat(captorFwIds.getAllValues().stream()
+                .flatMap(x -> Arrays.stream(x).boxed())
+                .collect(Collectors.toList()))
+                .containsExactly(2, 3);
     }
 
     @Test
@@ -193,7 +240,8 @@
         mLogger.logOnEnrolled(2 /* targetUserId */,
                 10 /* latency */,
                 true /* enrollSuccessful */,
-                30 /* source */);
+                30 /* source */,
+                1 /* templateId */);
         mLogger.logOnError(mContext, mOpContext,
                 4 /* error */,
                 0 /* vendorCode */,
@@ -207,7 +255,7 @@
                 anyLong(), anyInt(), anyBoolean(), anyInt(), anyFloat());
         verify(mSink, never()).enroll(
                 anyInt(), anyInt(), anyInt(), anyInt(), anyLong(), anyBoolean(), anyFloat(),
-                anyInt());
+                anyInt(), anyInt());
         verify(mSink, never()).error(eq(mOpContext),
                 anyInt(), anyInt(), anyInt(), anyBoolean(),
                 anyLong(), anyInt(), anyInt(), anyInt());
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerOperationTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerOperationTest.java
index 4f07380..26e2614 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerOperationTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerOperationTest.java
@@ -147,7 +147,7 @@
         when(mInterruptableClientMonitor.getFreshDaemon()).thenReturn(mHal);
 
         assertThat(mInterruptableOperation.startWithCookie(mOnStartCallback, cookie)).isTrue();
-        assertThat(mInterruptableOperation.startWithCookie(mOnStartCallback, cookie)).isFalse();
+        assertThat(mInterruptableOperation.startWithCookie(mOnStartCallback, cookie)).isTrue();
     }
 
     @Test
@@ -201,7 +201,7 @@
         when(mInterruptableClientMonitor.getFreshDaemon()).thenReturn(mHal);
 
         assertThat(mInterruptableOperation.start(mOnStartCallback)).isTrue();
-        assertThat(mInterruptableOperation.start(mOnStartCallback)).isFalse();
+        assertThat(mInterruptableOperation.start(mOnStartCallback)).isTrue();
     }
 
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java
index 90c07d4..fd8c792 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java
@@ -37,6 +37,7 @@
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
+import static org.mockito.kotlin.StubberKt.doReturn;
 
 import android.content.Context;
 import android.hardware.biometrics.AuthenticateOptions;
@@ -810,14 +811,16 @@
         final ClientMonitorCallback callback0 = mock(ClientMonitorCallback.class);
         final ClientMonitorCallback callback1 = mock(ClientMonitorCallback.class);
         final ClientMonitorCallback callback2 = mock(ClientMonitorCallback.class);
+        final BiometricLogger logger = mock(BiometricLogger.class);
+        doReturn(logger).when(logger).swapAction(any(), anyInt());
 
         final TestInternalCleanupClient client1 = new
                 TestInternalCleanupClient(mContext, daemon, userId,
-                owner, TEST_SENSOR_ID, mock(BiometricLogger.class),
+                owner, TEST_SENSOR_ID, logger,
                 mBiometricContext, utils, authenticatorIds);
         final TestInternalCleanupClient client2 = new
                 TestInternalCleanupClient(mContext, daemon, userId,
-                owner, TEST_SENSOR_ID, mock(BiometricLogger.class),
+                owner, TEST_SENSOR_ID, logger,
                 mBiometricContext, utils, authenticatorIds);
 
         //add initial start client to scheduler, so later clients will be on pending operation queue
@@ -1248,9 +1251,9 @@
                 @Nullable ClientMonitorCallbackConverter listener, int[] biometricIds, int userId,
                 @NonNull String owner, @NonNull BiometricUtils<Fingerprint> utils, int sensorId,
                 @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
-                @NonNull Map<Integer, Long> authenticatorIds) {
+                @NonNull Map<Integer, Long> authenticatorIds, int reason) {
             super(context, lazyDaemon, token, listener, userId, owner, utils, sensorId,
-                    logger, biometricContext, authenticatorIds);
+                    logger, biometricContext, authenticatorIds, reason);
         }
 
         @Override
@@ -1289,10 +1292,10 @@
                 Supplier<Object> lazyDaemon, IBinder token, int biometricId, int userId,
                 String owner, BiometricUtils<Fingerprint> utils, int sensorId,
                 @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
-                Map<Integer, Long> authenticatorIds) {
+                Map<Integer, Long> authenticatorIds, int reason) {
             return new TestRemovalClient(context, lazyDaemon, token, null,
                     new int[]{biometricId}, userId, owner, utils, sensorId, logger,
-                    biometricContext, authenticatorIds);
+                    biometricContext, authenticatorIds, reason);
         }
 
         @Override
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClientTest.java
index 6ac95c8..276da39 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClientTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClientTest.java
@@ -197,7 +197,7 @@
         client.onEnrollResult(new Face("face", 1 /* faceId */, 20 /* deviceId */), 0);
 
         verify(mBiometricLogger).logOnEnrolled(anyInt(), anyLong(), anyBoolean(),
-                eq(BiometricsProtoEnums.ENROLLMENT_SOURCE_SUW));
+                eq(BiometricsProtoEnums.ENROLLMENT_SOURCE_SUW), eq(1));
     }
 
     private FaceEnrollClient createClient() throws RemoteException {
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceInternalEnumerateClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceInternalEnumerateClientTest.java
index d8bdd50..734ee16 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceInternalEnumerateClientTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceInternalEnumerateClientTest.java
@@ -21,6 +21,7 @@
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyList;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.spy;
@@ -28,6 +29,7 @@
 import static org.mockito.Mockito.when;
 
 import android.content.Context;
+import android.hardware.biometrics.BiometricsProtoEnums;
 import android.hardware.biometrics.face.ISession;
 import android.hardware.face.Face;
 import android.os.IBinder;
@@ -44,12 +46,15 @@
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
+import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 import org.mockito.junit.MockitoJUnit;
 import org.mockito.junit.MockitoRule;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
+import java.util.stream.Collectors;
 
 @Presubmit
 @SmallTest
@@ -104,6 +109,8 @@
             mClient.onEnumerationResult(mFace, 0);
             return null;
         }).when(mSession).enumerateEnrollments();
+        final ArgumentCaptor<int[]> captorHalIds = ArgumentCaptor.forClass(int[].class);
+        final ArgumentCaptor<int[]> captorFwIds = ArgumentCaptor.forClass(int[].class);
 
         mClient.start(mCallback);
 
@@ -111,6 +118,17 @@
         assertThat(mClient.getUnknownHALTemplates().size()).isEqualTo(0);
         assertThat(mNotificationSent).isFalse();
         verify(mBiometricUtils, never()).removeBiometricForUser(any(), anyInt(), anyInt());
+        verify(mBiometricLogger).logOnEnumerated(eq(USER_ID),
+                eq(BiometricsProtoEnums.ENUMERATION_RESULT_OK), captorHalIds.capture(),
+                captorFwIds.capture());
+        assertThat(captorHalIds.getAllValues().stream()
+                .flatMap(x -> Arrays.stream(x).boxed())
+                .collect(Collectors.toList()))
+                .containsExactly(mBiometricId);
+        assertThat(captorFwIds.getAllValues().stream()
+                .flatMap(x -> Arrays.stream(x).boxed())
+                .collect(Collectors.toList()))
+                .containsExactly(mBiometricId);
         verify(mCallback).onClientFinished(mClient, true);
     }
 
@@ -171,6 +189,8 @@
             mClient.onEnumerationResult(identifier, 0);
             return null;
         }).when(mSession).enumerateEnrollments();
+        final ArgumentCaptor<int[]> captorHalIds = ArgumentCaptor.forClass(int[].class);
+        final ArgumentCaptor<int[]> captorFwIds = ArgumentCaptor.forClass(int[].class);
 
         mClient.start(mCallback);
 
@@ -178,6 +198,17 @@
         assertThat(mClient.getUnknownHALTemplates().size()).isEqualTo(1);
         assertThat(mNotificationSent).isTrue();
         verify(mBiometricUtils).removeBiometricForUser(mContext, USER_ID, mBiometricId);
+        verify(mBiometricLogger).logOnEnumerated(eq(USER_ID),
+                eq(BiometricsProtoEnums.ENUMERATION_RESULT_DANGLING_BOTH), captorHalIds.capture(),
+                captorFwIds.capture());
+        assertThat(captorHalIds.getAllValues().stream()
+                .flatMap(x -> Arrays.stream(x).boxed())
+                .collect(Collectors.toList()))
+                .containsExactly(2);
+        assertThat(captorFwIds.getAllValues().stream()
+                .flatMap(x -> Arrays.stream(x).boxed())
+                .collect(Collectors.toList()))
+                .containsExactly(mBiometricId);
         verify(mCallback).onClientFinished(mClient, true);
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceRemovalClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceRemovalClientTest.java
index 1d9e933..c224af2 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceRemovalClientTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceRemovalClientTest.java
@@ -25,6 +25,7 @@
 
 import android.hardware.biometrics.BiometricAuthenticator;
 import android.hardware.biometrics.BiometricConstants;
+import android.hardware.biometrics.BiometricsProtoEnums;
 import android.hardware.biometrics.face.ISession;
 import android.hardware.face.Face;
 import android.os.IBinder;
@@ -119,6 +120,6 @@
         return new FaceRemovalClient(mContext, () -> aidl, mToken,
                 mClientMonitorCallbackConverter, biometricIds, USER_ID,
                 "own-it", mUtils /* utils */, 5 /* sensorId */, mBiometricLogger, mBiometricContext,
-                mAuthenticatorIds);
+                mAuthenticatorIds, BiometricsProtoEnums.UNENROLL_REASON_UNKNOWN);
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClientTest.java
index 5c6513d..ea96d19 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClientTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClientTest.java
@@ -309,7 +309,7 @@
         client.onEnrollResult(new Fingerprint("fingerprint", 1 /* faceId */, 20 /* deviceId */), 0);
 
         verify(mBiometricLogger).logOnEnrolled(anyInt(), anyLong(), anyBoolean(),
-                eq(BiometricsProtoEnums.ENROLLMENT_SOURCE_SUW));
+                eq(BiometricsProtoEnums.ENROLLMENT_SOURCE_SUW), eq(1));
     }
 
     private void showHideOverlay(
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalCleanupClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalCleanupClientTest.java
index 7dcf841..a6604d1 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalCleanupClientTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalCleanupClientTest.java
@@ -18,6 +18,7 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.eq;
@@ -99,6 +100,7 @@
         mAddedIds = new ArrayList<>();
 
         when(mAidlSession.getSession()).thenReturn(mSession);
+        doReturn(mLogger).when(mLogger).swapAction(any(), anyInt());
     }
 
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalEnumerateClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalEnumerateClientTest.java
index fab1200..a809c3b 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalEnumerateClientTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalEnumerateClientTest.java
@@ -21,6 +21,7 @@
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyList;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.spy;
@@ -28,6 +29,7 @@
 import static org.mockito.Mockito.when;
 
 import android.content.Context;
+import android.hardware.biometrics.BiometricsProtoEnums;
 import android.hardware.biometrics.fingerprint.ISession;
 import android.hardware.fingerprint.Fingerprint;
 import android.os.IBinder;
@@ -44,11 +46,13 @@
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
+import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 import org.mockito.junit.MockitoJUnit;
 import org.mockito.junit.MockitoRule;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
@@ -108,6 +112,8 @@
             mClient.onEnumerationResult(new Fingerprint("three", 3, 1), 0);
             return null;
         }).when(mSession).enumerateEnrollments();
+        final ArgumentCaptor<int[]> captorHalIds = ArgumentCaptor.forClass(int[].class);
+        final ArgumentCaptor<int[]> captorFwIds = ArgumentCaptor.forClass(int[].class);
         mClient.start(mCallback);
 
         verify(mSession).enumerateEnrollments();
@@ -116,6 +122,17 @@
                 .collect(Collectors.toList())).containsExactly(2, 3);
         assertThat(mNotificationSent).isTrue();
         verify(mBiometricUtils).removeBiometricForUser(mContext, USER_ID, 1);
+        verify(mBiometricLogger).logOnEnumerated(eq(USER_ID),
+                eq(BiometricsProtoEnums.ENUMERATION_RESULT_DANGLING_BOTH), captorHalIds.capture(),
+                captorFwIds.capture());
+        assertThat(captorHalIds.getAllValues().stream()
+                .flatMap(x -> Arrays.stream(x).boxed())
+                .collect(Collectors.toList()))
+                .containsExactly(2, 3);
+        assertThat(captorFwIds.getAllValues().stream()
+                .flatMap(x -> Arrays.stream(x).boxed())
+                .collect(Collectors.toList()))
+                .containsExactly(1);
         verify(mCallback).onClientFinished(mClient, true);
     }
 
@@ -125,12 +142,25 @@
             mClient.onEnumerationResult(new Fingerprint("one", 1, 1), 0);
             return null;
         }).when(mSession).enumerateEnrollments();
+        final ArgumentCaptor<int[]> captorHalIds = ArgumentCaptor.forClass(int[].class);
+        final ArgumentCaptor<int[]> captorFwIds = ArgumentCaptor.forClass(int[].class);
         mClient.start(mCallback);
 
         verify(mSession).enumerateEnrollments();
         assertThat(mClient.getUnknownHALTemplates().size()).isEqualTo(0);
         assertThat(mNotificationSent).isFalse();
         verify(mBiometricUtils, never()).removeBiometricForUser(any(), anyInt(), anyInt());
+        verify(mBiometricLogger).logOnEnumerated(eq(USER_ID),
+                eq(BiometricsProtoEnums.ENUMERATION_RESULT_OK), captorHalIds.capture(),
+                captorFwIds.capture());
+        assertThat(captorHalIds.getAllValues().stream()
+                .flatMap(x -> Arrays.stream(x).boxed())
+                .collect(Collectors.toList()))
+                .containsExactly(1);
+        assertThat(captorFwIds.getAllValues().stream()
+                .flatMap(x -> Arrays.stream(x).boxed())
+                .collect(Collectors.toList()))
+                .containsExactly(1);
         verify(mCallback).onClientFinished(mClient, true);
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintRemovalClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintRemovalClientTest.java
index 64f07e2..ced916e 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintRemovalClientTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintRemovalClientTest.java
@@ -26,6 +26,7 @@
 import static org.mockito.Mockito.when;
 
 import android.content.Context;
+import android.hardware.biometrics.BiometricsProtoEnums;
 import android.hardware.biometrics.fingerprint.ISession;
 import android.hardware.fingerprint.Fingerprint;
 import android.os.IBinder;
@@ -91,7 +92,8 @@
 
         mClient = new FingerprintRemovalClient(mContext, () -> mAidlSession, mToken, mListener,
                 mBiometricIds, USER_ID, TAG, mBiometricUtils, SENSOR_ID,
-                mBiometricLogger, mBiometricContext, mAuthenticatorIds);
+                mBiometricLogger, mBiometricContext, mAuthenticatorIds,
+                BiometricsProtoEnums.UNENROLL_REASON_UNKNOWN);
     }
 
     @Test
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 a0005d9..3360e1d 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
@@ -413,7 +413,8 @@
                 .isEqualTo(Constants.HANDLED);
         assertThat(mHdmiCecLocalDevicePlayback.isActiveSource()).isFalse();
         // After 30s of device inactivity, device would go to sleep.
-        skipActiveSourceLostUi(STANDBY_AFTER_ACTIVE_SOURCE_LOST_DELAY_MS);
+        skipActiveSourceLostUi(STANDBY_AFTER_ACTIVE_SOURCE_LOST_DELAY_MS, false,
+                false);
         assertThat(mPowerManager.isInteractive()).isFalse();
     }
 
@@ -422,9 +423,8 @@
         int newPlaybackPhysicalAddress = 0x2100;
         int switchPhysicalAddress = 0x2000;
         mNativeWrapper.setPhysicalAddress(newPlaybackPhysicalAddress);
-        mHdmiControlService.onHotplug(newPlaybackPhysicalAddress, true);
         mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
-
+        mHdmiControlService.onHotplug(newPlaybackPhysicalAddress, true);
         mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setStringValue(
                 HdmiControlManager.CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST,
                 HdmiControlManager.POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_STANDBY_NOW);
@@ -457,7 +457,8 @@
                 .isEqualTo(Constants.HANDLED);
         assertThat(mHdmiCecLocalDevicePlayback.isActiveSource()).isFalse();
         // After 30s of device inactivity, device would go to sleep.
-        skipActiveSourceLostUi(STANDBY_AFTER_ACTIVE_SOURCE_LOST_DELAY_MS);
+        skipActiveSourceLostUi(STANDBY_AFTER_ACTIVE_SOURCE_LOST_DELAY_MS, false,
+                false);
         assertThat(mPowerManager.isInteractive()).isFalse();
     }
 
@@ -617,7 +618,8 @@
                 .isEqualTo(Constants.HANDLED);
         assertThat(mHdmiCecLocalDevicePlayback.isActiveSource()).isFalse();
         // After 30s of device inactivity, device would go to sleep.
-        skipActiveSourceLostUi(STANDBY_AFTER_ACTIVE_SOURCE_LOST_DELAY_MS);
+        skipActiveSourceLostUi(STANDBY_AFTER_ACTIVE_SOURCE_LOST_DELAY_MS, false,
+                false);
         assertThat(mPowerManager.isInteractive()).isFalse();
     }
 
@@ -722,7 +724,8 @@
         assertThat(mHdmiCecLocalDevicePlayback.getActiveSource().logicalAddress).isEqualTo(
                 ADDR_INVALID);
         // After 30s of device inactivity, device would go to sleep.
-        skipActiveSourceLostUi(STANDBY_AFTER_ACTIVE_SOURCE_LOST_DELAY_MS);
+        skipActiveSourceLostUi(STANDBY_AFTER_ACTIVE_SOURCE_LOST_DELAY_MS,true,
+                true);
         assertThat(mPowerManager.isInteractive()).isFalse();
     }
 
@@ -1265,14 +1268,35 @@
         assertThat(mHdmiCecLocalDevicePlayback.handleActiveSource(message))
                 .isEqualTo(Constants.HANDLED);
         mTestLooper.dispatchAll();
-
-        // After 30s of device inactivity, device would go to sleep.
-        skipActiveSourceLostUi(STANDBY_AFTER_ACTIVE_SOURCE_LOST_DELAY_MS);
+        // After 30s of device inactivity, device would assert active source.
+        skipActiveSourceLostUi(STANDBY_AFTER_ACTIVE_SOURCE_LOST_DELAY_MS, true,
+                true);
         assertThat(mPowerManager.isInteractive()).isFalse();
         assertThat(mHdmiCecLocalDevicePlayback.isActiveSource()).isFalse();
     }
 
     @Test
+    public void handleActiveSourceFromTv_tvNotAnswerRequest_assertActiveSource() {
+        mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setStringValue(
+                HdmiControlManager.CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST,
+                HdmiControlManager.POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_STANDBY_NOW);
+        mPowerManager.setInteractive(true);
+        HdmiCecMessage message = HdmiCecMessageBuilder.buildActiveSource(mPlaybackLogicalAddress,
+                mPlaybackPhysicalAddress);
+        assertThat(mHdmiCecLocalDevicePlayback.handleActiveSource(message))
+                .isEqualTo(Constants.HANDLED);
+        message = HdmiCecMessageBuilder.buildActiveSource(ADDR_TV, 0x0000);
+        assertThat(mHdmiCecLocalDevicePlayback.handleActiveSource(message))
+                .isEqualTo(Constants.HANDLED);
+        mTestLooper.dispatchAll();
+        // After 30s of device inactivity, device would assert active source.
+        skipActiveSourceLostUi(STANDBY_AFTER_ACTIVE_SOURCE_LOST_DELAY_MS, true,
+                false);
+        assertThat(mPowerManager.isInteractive()).isTrue();
+        assertThat(mHdmiCecLocalDevicePlayback.isActiveSource()).isTrue();
+    }
+
+    @Test
     public void handleActiveSource_otherDevice_ActiveSource_mediaSessionsPaused() {
         mHdmiCecLocalDevicePlayback.setActiveSource(mPlaybackLogicalAddress,
                 mPlaybackPhysicalAddress, "HdmiCecLocalDevicePlaybackTest");
@@ -1343,7 +1367,8 @@
                 .isEqualTo(Constants.HANDLED);
         assertThat(mHdmiCecLocalDevicePlayback.isActiveSource()).isFalse();
         // After 30s of device inactivity, device would go to sleep.
-        skipActiveSourceLostUi(STANDBY_AFTER_ACTIVE_SOURCE_LOST_DELAY_MS);
+        skipActiveSourceLostUi(STANDBY_AFTER_ACTIVE_SOURCE_LOST_DELAY_MS, true,
+                true);
         assertThat(mPowerManager.isInteractive()).isFalse();
         mHdmiControlService.onStandby(HdmiControlService.STANDBY_SCREEN_OFF);
         // 3. DUT becomes <AS> again.
@@ -1704,9 +1729,9 @@
         assertThat(mHdmiCecLocalDevicePlayback.handleSetStreamPath(message))
                 .isEqualTo(Constants.HANDLED);
         assertThat(mHdmiCecLocalDevicePlayback.isActiveSource()).isFalse();
-
         // After 30s of device inactivity, device would go to sleep.
-        skipActiveSourceLostUi(STANDBY_AFTER_ACTIVE_SOURCE_LOST_DELAY_MS);
+        skipActiveSourceLostUi(STANDBY_AFTER_ACTIVE_SOURCE_LOST_DELAY_MS, false,
+                false);
         assertThat(mPowerManager.isInteractive()).isFalse();
     }
 
@@ -2323,11 +2348,197 @@
         mTestLooper.dispatchAll();
 
         // After 30s of device inactivity, device would go to sleep.
-        skipActiveSourceLostUi(STANDBY_AFTER_ACTIVE_SOURCE_LOST_DELAY_MS);
+        skipActiveSourceLostUi(STANDBY_AFTER_ACTIVE_SOURCE_LOST_DELAY_MS, true,
+                true);
         assertThat(mPowerManager.isInteractive()).isFalse();
     }
 
     @Test
+    public void onActiveSourceLostToTv_requestActiveSourceAnsweredFromTv_showPopup() {
+        mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setStringValue(
+                HdmiControlManager.CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST,
+                HdmiControlManager.POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_STANDBY_NOW);
+
+        mHdmiCecLocalDevicePlayback.setActiveSource(mPlaybackLogicalAddress,
+                mPlaybackPhysicalAddress, "HdmiCecLocalDevicePlaybackTest");
+        mPowerManager.setInteractive(true);
+        mNativeWrapper.clearResultMessages();
+        mTestLooper.dispatchAll();
+
+        HdmiCecMessage activeSourceFromTv =
+                HdmiCecMessageBuilder.buildActiveSource(ADDR_TV, 0x0000);
+        HdmiCecMessage requestActiveSource =
+                HdmiCecMessageBuilder.buildRequestActiveSource(mPlaybackLogicalAddress);
+
+        assertThat(mHdmiCecLocalDevicePlayback.handleActiveSource(activeSourceFromTv))
+                .isEqualTo(Constants.HANDLED);
+        mTestLooper.dispatchAll();
+
+        mTestLooper.moveTimeForward(POPUP_AFTER_ACTIVE_SOURCE_LOST_DELAY_MS);
+        mTestLooper.dispatchAll();
+
+        // RequestActiveSourceAction is triggered.
+        assertThat(mNativeWrapper.getResultMessages()).contains(requestActiveSource);
+        mTestLooper.moveTimeForward(TIMEOUT_MS);
+        mTestLooper.dispatchAll();
+
+        assertThat(mNativeWrapper.getResultMessages()).contains(requestActiveSource);
+        mNativeWrapper.onCecMessage(activeSourceFromTv);
+        mTestLooper.moveTimeForward(TIMEOUT_MS);
+        mTestLooper.dispatchAll();
+
+        assertThat(mHdmiCecLocalDevicePlayback.getActiveSource().logicalAddress).isEqualTo(ADDR_TV);
+        assertThat(mIsOnActiveSourceLostPopupActive).isTrue();
+    }
+
+    @Test
+    public void onActiveSourceLostToTv_requestActiveSourceNotAnswered_assertActiveSource() {
+        mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setStringValue(
+                HdmiControlManager.CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST,
+                HdmiControlManager.POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_STANDBY_NOW);
+
+        mHdmiCecLocalDevicePlayback.setActiveSource(mPlaybackLogicalAddress,
+                mPlaybackPhysicalAddress, "HdmiCecLocalDevicePlaybackTest");
+        mPowerManager.setInteractive(true);
+        mNativeWrapper.clearResultMessages();
+        mTestLooper.dispatchAll();
+
+        HdmiCecMessage activeSourceFromTv =
+                HdmiCecMessageBuilder.buildActiveSource(ADDR_TV, 0x0000);
+        HdmiCecMessage requestActiveSource =
+                HdmiCecMessageBuilder.buildRequestActiveSource(mPlaybackLogicalAddress);
+        HdmiCecMessage activeSourceFromPlayback =
+                HdmiCecMessageBuilder.buildActiveSource(mPlaybackLogicalAddress,
+                        mPlaybackPhysicalAddress);
+        assertThat(mHdmiCecLocalDevicePlayback.handleActiveSource(activeSourceFromTv))
+                .isEqualTo(Constants.HANDLED);
+        assertThat(mHdmiCecLocalDevicePlayback.getActiveSource().logicalAddress).isEqualTo(ADDR_TV);
+        mTestLooper.dispatchAll();
+
+        mTestLooper.moveTimeForward(POPUP_AFTER_ACTIVE_SOURCE_LOST_DELAY_MS);
+        mTestLooper.dispatchAll();
+
+        // RequestActiveSourceAction is triggered.
+        assertThat(mNativeWrapper.getResultMessages()).contains(requestActiveSource);
+        mTestLooper.moveTimeForward(TIMEOUT_MS);
+        mTestLooper.dispatchAll();
+
+        assertThat(mNativeWrapper.getResultMessages()).contains(requestActiveSource);
+        mTestLooper.moveTimeForward(TIMEOUT_MS);
+        mTestLooper.dispatchAll();
+
+        // Pop-up is not shown, playback device asserts active source since TV doesn't answer the
+        // request.
+        assertThat(mIsOnActiveSourceLostPopupActive).isFalse();
+        assertThat(mNativeWrapper.getResultMessages().contains(activeSourceFromPlayback)).isTrue();
+        assertThat(mHdmiCecLocalDevicePlayback.getActiveSource().logicalAddress)
+                .isEqualTo(mPlaybackLogicalAddress);
+        assertThat(mHdmiCecLocalDevicePlayback.getActiveSource().physicalAddress)
+                .isEqualTo(mPlaybackPhysicalAddress);
+    }
+
+    @Test
+    public void onActiveSourceLost_requestActiveSourceNotAnswered_playbackIsAS_dontShowPopup() {
+        mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setStringValue(
+                HdmiControlManager.CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST,
+                HdmiControlManager.POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_STANDBY_NOW);
+
+        mHdmiCecLocalDevicePlayback.setActiveSource(mPlaybackLogicalAddress,
+                mPlaybackPhysicalAddress, "HdmiCecLocalDevicePlaybackTest");
+        mPowerManager.setInteractive(true);
+        mNativeWrapper.clearResultMessages();
+        mTestLooper.dispatchAll();
+
+        HdmiCecMessage activeSourceFromTv =
+                HdmiCecMessageBuilder.buildActiveSource(ADDR_TV, 0x0000);
+        HdmiCecMessage requestActiveSource =
+                HdmiCecMessageBuilder.buildRequestActiveSource(mPlaybackLogicalAddress);
+        HdmiCecMessage setStreamPathToPlayback = HdmiCecMessageBuilder.buildSetStreamPath(ADDR_TV,
+                mPlaybackPhysicalAddress);
+        HdmiCecMessage activeSourceFromPlayback =
+                HdmiCecMessageBuilder.buildActiveSource(mPlaybackLogicalAddress,
+                        mPlaybackPhysicalAddress);
+        assertThat(mHdmiCecLocalDevicePlayback.handleActiveSource(activeSourceFromTv))
+                .isEqualTo(Constants.HANDLED);
+        assertThat(mHdmiCecLocalDevicePlayback.getActiveSource().logicalAddress).isEqualTo(ADDR_TV);
+        mTestLooper.dispatchAll();
+
+        mTestLooper.moveTimeForward(POPUP_AFTER_ACTIVE_SOURCE_LOST_DELAY_MS);
+        mTestLooper.dispatchAll();
+
+        // RequestActiveSourceAction is triggered.
+        assertThat(mNativeWrapper.getResultMessages()).contains(requestActiveSource);
+        mTestLooper.moveTimeForward(TIMEOUT_MS);
+        mTestLooper.dispatchAll();
+
+        assertThat(mHdmiCecLocalDevicePlayback.handleSetStreamPath(setStreamPathToPlayback))
+                .isEqualTo(Constants.HANDLED);
+        mTestLooper.dispatchAll();
+
+        mTestLooper.moveTimeForward(TIMEOUT_MS);
+        mTestLooper.dispatchAll();
+
+        // Pop-up is not shown since playback device is active source.
+        assertThat(mIsOnActiveSourceLostPopupActive).isFalse();
+        assertThat(mNativeWrapper.getResultMessages().contains(activeSourceFromPlayback)).isTrue();
+        assertThat(mHdmiCecLocalDevicePlayback.getActiveSource().logicalAddress)
+                .isEqualTo(mPlaybackLogicalAddress);
+        assertThat(mHdmiCecLocalDevicePlayback.getActiveSource().physicalAddress)
+                .isEqualTo(mPlaybackPhysicalAddress);
+    }
+
+    @Test
+    public void onActiveSourceLost_requestASNotAnswered_setStreamPathToNonCecInput_dontShowPopup() {
+        int otherPhysicalAddress = mPlaybackPhysicalAddress + 0x0100;
+        mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setStringValue(
+                HdmiControlManager.CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST,
+                HdmiControlManager.POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_STANDBY_NOW);
+
+        mHdmiCecLocalDevicePlayback.setActiveSource(mPlaybackLogicalAddress,
+                mPlaybackPhysicalAddress, "HdmiCecLocalDevicePlaybackTest");
+        mPowerManager.setInteractive(true);
+        mNativeWrapper.clearResultMessages();
+        mTestLooper.dispatchAll();
+
+        HdmiCecMessage activeSourceFromTv =
+                HdmiCecMessageBuilder.buildActiveSource(ADDR_TV, 0x0000);
+        HdmiCecMessage requestActiveSource =
+                HdmiCecMessageBuilder.buildRequestActiveSource(mPlaybackLogicalAddress);
+        HdmiCecMessage setStreamPathToOtherInput = HdmiCecMessageBuilder.buildSetStreamPath(ADDR_TV,
+                otherPhysicalAddress);
+        HdmiCecMessage activeSourceFromPlayback =
+                HdmiCecMessageBuilder.buildActiveSource(mPlaybackLogicalAddress,
+                        mPlaybackPhysicalAddress);
+        assertThat(mHdmiCecLocalDevicePlayback.handleActiveSource(activeSourceFromTv))
+                .isEqualTo(Constants.HANDLED);
+        assertThat(mHdmiCecLocalDevicePlayback.getActiveSource().logicalAddress).isEqualTo(ADDR_TV);
+        mTestLooper.dispatchAll();
+
+        mTestLooper.moveTimeForward(POPUP_AFTER_ACTIVE_SOURCE_LOST_DELAY_MS);
+        mTestLooper.dispatchAll();
+
+        // RequestActiveSourceAction is triggered.
+        assertThat(mNativeWrapper.getResultMessages()).contains(requestActiveSource);
+        mTestLooper.moveTimeForward(TIMEOUT_MS);
+        mTestLooper.dispatchAll();
+
+        assertThat(mHdmiCecLocalDevicePlayback.handleSetStreamPath(setStreamPathToOtherInput))
+                .isEqualTo(Constants.HANDLED);
+        mTestLooper.dispatchAll();
+
+        mTestLooper.moveTimeForward(TIMEOUT_MS);
+        mTestLooper.dispatchAll();
+
+        // Pop-up is shown, playback device doesn't assert active source since active path is
+        // switched to a non-CEC device.
+        assertThat(mIsOnActiveSourceLostPopupActive).isTrue();
+        assertThat(mHdmiCecLocalDevicePlayback.getActiveSource().logicalAddress)
+                .isEqualTo(ADDR_INVALID);
+        assertThat(mHdmiCecLocalDevicePlayback.getActiveSource().physicalAddress)
+                .isEqualTo(otherPhysicalAddress);
+    }
+
+    @Test
     public void onActiveSourceLost_interactionWithDut_noStandbyAfterTimeout() {
         mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setStringValue(
                 HdmiControlManager.CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST,
@@ -2350,7 +2561,7 @@
         mTestLooper.dispatchAll();
 
         // User interacted with the DUT, so the device will not go to standby.
-        skipActiveSourceLostUi(0);
+        skipActiveSourceLostUi(0, true, true);
         assertThat(mIsOnActiveSourceLostPopupActive).isFalse();
         assertThat(mPowerManager.isInteractive()).isTrue();
         assertThat(mNativeWrapper.getResultMessages().contains(activeSourceFromPlayback)).isTrue();
@@ -2387,6 +2598,10 @@
         // Pop-up is triggered.
         mTestLooper.moveTimeForward(POPUP_AFTER_ACTIVE_SOURCE_LOST_DELAY_MS);
         mTestLooper.dispatchAll();
+        // RequestActiveSourceAction is triggered and TV confirms active source.
+        mNativeWrapper.onCecMessage(activeSourceFromTv);
+        mTestLooper.dispatchAll();
+
         assertThat(mIsOnActiveSourceLostPopupActive).isTrue();
 
         assertThat(mHdmiCecLocalDevicePlayback.handleSetStreamPath(setStreamPathToPlayback))
@@ -2407,6 +2622,8 @@
 
     @Test
     public void onActiveSourceLost_incomingRoutingChangeToDut_noStandbyAfterTimeout() {
+        int otherPlaybackLogicalAddress = mPlaybackLogicalAddress == Constants.ADDR_PLAYBACK_2
+                ? Constants.ADDR_PLAYBACK_1 : Constants.ADDR_PLAYBACK_2;
         mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setStringValue(
                 HdmiControlManager.CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST,
                 HdmiControlManager.POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_STANDBY_NOW);
@@ -2420,18 +2637,21 @@
         mNativeWrapper.clearResultMessages();
         mTestLooper.dispatchAll();
 
-        HdmiCecMessage activeSourceFromTv =
-                HdmiCecMessageBuilder.buildActiveSource(ADDR_TV, 0x0000);
+        HdmiCecMessage activeSourceFromOtherPlayback =
+                HdmiCecMessageBuilder.buildActiveSource(otherPlaybackLogicalAddress,
+                        mPlaybackPhysicalAddress + 0x0100);
         HdmiCecMessage activeSourceFromPlayback =
                 HdmiCecMessageBuilder.buildActiveSource(mPlaybackLogicalAddress,
                         mPlaybackPhysicalAddress);
         HdmiCecMessage routingChangeToPlayback =
-                HdmiCecMessageBuilder.buildRoutingChange(ADDR_TV, 0x0000,
+                HdmiCecMessageBuilder.buildRoutingChange(ADDR_TV,
+                        mPlaybackPhysicalAddress + 0x0100,
                         mPlaybackPhysicalAddress);
 
-        assertThat(mHdmiCecLocalDevicePlayback.handleActiveSource(activeSourceFromTv))
+        assertThat(mHdmiCecLocalDevicePlayback.handleActiveSource(activeSourceFromOtherPlayback))
                 .isEqualTo(Constants.HANDLED);
-        assertThat(mHdmiCecLocalDevicePlayback.getActiveSource().logicalAddress).isEqualTo(ADDR_TV);
+        assertThat(mHdmiCecLocalDevicePlayback.getActiveSource().logicalAddress).isEqualTo(
+                otherPlaybackLogicalAddress);
         mTestLooper.dispatchAll();
 
         // Pop-up is triggered.
@@ -2600,13 +2820,30 @@
         assertThat(mPowerManager.isInteractive()).isFalse();
     }
 
-    private void skipActiveSourceLostUi(long idleDuration) {
+    private void skipActiveSourceLostUi(long idleDuration, boolean activeSourceLostToTv,
+            boolean tvAnswersRequest) {
         mTestLooper.moveTimeForward(POPUP_AFTER_ACTIVE_SOURCE_LOST_DELAY_MS);
         mTestLooper.dispatchAll();
+        if (activeSourceLostToTv) {
+            // RequestActiveSourceAction is triggered.
+            mTestLooper.moveTimeForward(TIMEOUT_MS);
+            mTestLooper.dispatchAll();
+            if (tvAnswersRequest) {
+                HdmiCecMessage activeSource = HdmiCecMessageBuilder.buildActiveSource(ADDR_TV,
+                        0x0000);
+                mNativeWrapper.onCecMessage(activeSource);
+                mTestLooper.dispatchAll();
+            } else {
+                mTestLooper.moveTimeForward(TIMEOUT_MS);
+                mTestLooper.dispatchAll();
+                assertThat(mIsOnActiveSourceLostPopupActive).isFalse();
+                return;
+            }
+        }
         assertThat(mIsOnActiveSourceLostPopupActive).isTrue();
 
         mPowerManagerInternal.setIdleDuration(idleDuration);
         mTestLooper.moveTimeForward(STANDBY_AFTER_ACTIVE_SOURCE_LOST_DELAY_MS);
         mTestLooper.dispatchAll();
     }
-}
+}
\ No newline at end of file
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 935c8b8..51276a4 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java
@@ -2234,6 +2234,25 @@
     }
 
     @Test
+    public void handleReportPhysicalAddress_samePathAsActiveSource_differentLA_newActiveSource() {
+        // This scenario can be reproduced if active source is hotplugged out and replaced with
+        // another device that might have another LA.
+        int physicalAddress = 0x1000;
+        mHdmiControlService.setActiveSource(Constants.ADDR_PLAYBACK_1, physicalAddress,
+                "HdmiControlServiceTest");
+        mHdmiCecLocalDeviceTv.setActivePath(physicalAddress);
+        HdmiCecMessage reportPhysicalAddressFromPlayback2 =
+                HdmiCecMessageBuilder.buildReportPhysicalAddressCommand(ADDR_PLAYBACK_2,
+                        physicalAddress, HdmiDeviceInfo.DEVICE_PLAYBACK);
+        HdmiCecMessage setStreamPath = HdmiCecMessageBuilder.buildSetStreamPath(ADDR_TV,
+                physicalAddress);
+        mNativeWrapper.onCecMessage(reportPhysicalAddressFromPlayback2);
+        mTestLooper.dispatchAll();
+
+        assertThat(mNativeWrapper.getResultMessages()).contains(setStreamPath);
+    }
+
+    @Test
     public void onOneTouchPlay_wakeUp_addCecDevice() {
         assertThat(mHdmiControlService.getHdmiCecNetwork().getDeviceInfoList(false))
                 .isEmpty();
diff --git a/services/tests/servicestests/src/com/android/server/integrity/parser/BinaryFileOperationsTest.java b/services/tests/servicestests/src/com/android/server/integrity/parser/BinaryFileOperationsTest.java
deleted file mode 100644
index 723b6c5..0000000
--- a/services/tests/servicestests/src/com/android/server/integrity/parser/BinaryFileOperationsTest.java
+++ /dev/null
@@ -1,116 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.integrity.parser;
-
-import static com.android.server.integrity.model.ComponentBitSize.VALUE_SIZE_BITS;
-import static com.android.server.integrity.parser.BinaryFileOperations.getBooleanValue;
-import static com.android.server.integrity.parser.BinaryFileOperations.getIntValue;
-import static com.android.server.integrity.parser.BinaryFileOperations.getStringValue;
-import static com.android.server.integrity.utils.TestUtils.getBits;
-import static com.android.server.integrity.utils.TestUtils.getBytes;
-import static com.android.server.integrity.utils.TestUtils.getValueBits;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.content.integrity.IntegrityUtils;
-
-import com.android.server.integrity.model.BitInputStream;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-import java.nio.charset.StandardCharsets;
-
-@RunWith(JUnit4.class)
-public class BinaryFileOperationsTest {
-
-    private static final String IS_NOT_HASHED = "0";
-    private static final String IS_HASHED = "1";
-    private static final String PACKAGE_NAME = "com.test.app";
-    private static final String APP_CERTIFICATE = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
-
-    @Test
-    public void testGetStringValue() throws IOException {
-        byte[] stringBytes =
-                getBytes(
-                        IS_NOT_HASHED
-                                + getBits(PACKAGE_NAME.length(), VALUE_SIZE_BITS)
-                                + getValueBits(PACKAGE_NAME));
-        BitInputStream inputStream = new BitInputStream(new ByteArrayInputStream(stringBytes));
-
-        String resultString = getStringValue(inputStream);
-
-        assertThat(resultString).isEqualTo(PACKAGE_NAME);
-    }
-
-    @Test
-    public void testGetHashedStringValue() throws IOException {
-        byte[] ruleBytes =
-                getBytes(
-                        IS_HASHED
-                                + getBits(APP_CERTIFICATE.length(), VALUE_SIZE_BITS)
-                                + getValueBits(APP_CERTIFICATE));
-        BitInputStream inputStream = new BitInputStream(new ByteArrayInputStream(ruleBytes));
-
-        String resultString = getStringValue(inputStream);
-
-        assertThat(resultString)
-                .isEqualTo(IntegrityUtils.getHexDigest(
-                        APP_CERTIFICATE.getBytes(StandardCharsets.UTF_8)));
-    }
-
-    @Test
-    public void testGetStringValue_withSizeAndHashingInfo() throws IOException {
-        byte[] ruleBytes = getBytes(getValueBits(PACKAGE_NAME));
-        BitInputStream inputStream = new BitInputStream(new ByteArrayInputStream(ruleBytes));
-
-        String resultString = getStringValue(inputStream,
-                PACKAGE_NAME.length(), /* isHashedValue= */false);
-
-        assertThat(resultString).isEqualTo(PACKAGE_NAME);
-    }
-
-    @Test
-    public void testGetIntValue() throws IOException {
-        int randomValue = 15;
-        byte[] ruleBytes = getBytes(getBits(randomValue, /* numOfBits= */ 32));
-        BitInputStream inputStream = new BitInputStream(new ByteArrayInputStream(ruleBytes));
-
-        assertThat(getIntValue(inputStream)).isEqualTo(randomValue);
-    }
-
-    @Test
-    public void testGetBooleanValue_true() throws IOException {
-        String booleanValue = "1";
-        byte[] ruleBytes = getBytes(booleanValue);
-        BitInputStream inputStream = new BitInputStream(new ByteArrayInputStream(ruleBytes));
-
-        assertThat(getBooleanValue(inputStream)).isEqualTo(true);
-    }
-
-    @Test
-    public void testGetBooleanValue_false() throws IOException {
-        String booleanValue = "0";
-        byte[] ruleBytes = getBytes(booleanValue);
-        BitInputStream inputStream = new BitInputStream(new ByteArrayInputStream(ruleBytes));
-
-        assertThat(getBooleanValue(inputStream)).isEqualTo(false);
-    }
-}
diff --git a/services/tests/servicestests/src/com/android/server/integrity/parser/RuleBinaryParserTest.java b/services/tests/servicestests/src/com/android/server/integrity/parser/RuleBinaryParserTest.java
deleted file mode 100644
index 03363a1..0000000
--- a/services/tests/servicestests/src/com/android/server/integrity/parser/RuleBinaryParserTest.java
+++ /dev/null
@@ -1,693 +0,0 @@
-/*
- * Copyright (C) 2019 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.integrity.parser;
-
-import static com.android.server.integrity.model.ComponentBitSize.ATOMIC_FORMULA_START;
-import static com.android.server.integrity.model.ComponentBitSize.COMPOUND_FORMULA_END;
-import static com.android.server.integrity.model.ComponentBitSize.COMPOUND_FORMULA_START;
-import static com.android.server.integrity.model.ComponentBitSize.CONNECTOR_BITS;
-import static com.android.server.integrity.model.ComponentBitSize.DEFAULT_FORMAT_VERSION;
-import static com.android.server.integrity.model.ComponentBitSize.EFFECT_BITS;
-import static com.android.server.integrity.model.ComponentBitSize.FORMAT_VERSION_BITS;
-import static com.android.server.integrity.model.ComponentBitSize.KEY_BITS;
-import static com.android.server.integrity.model.ComponentBitSize.OPERATOR_BITS;
-import static com.android.server.integrity.model.ComponentBitSize.SEPARATOR_BITS;
-import static com.android.server.integrity.model.ComponentBitSize.VALUE_SIZE_BITS;
-import static com.android.server.integrity.utils.TestUtils.getBits;
-import static com.android.server.integrity.utils.TestUtils.getBytes;
-import static com.android.server.integrity.utils.TestUtils.getValueBits;
-import static com.android.server.testutils.TestUtils.assertExpectException;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.content.integrity.AtomicFormula;
-import android.content.integrity.CompoundFormula;
-import android.content.integrity.IntegrityUtils;
-import android.content.integrity.Rule;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-import java.nio.ByteBuffer;
-import java.nio.charset.StandardCharsets;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-
-@RunWith(JUnit4.class)
-public class RuleBinaryParserTest {
-
-    private static final String COMPOUND_FORMULA_START_BITS =
-            getBits(COMPOUND_FORMULA_START, SEPARATOR_BITS);
-    private static final String COMPOUND_FORMULA_END_BITS =
-            getBits(COMPOUND_FORMULA_END, SEPARATOR_BITS);
-    private static final String ATOMIC_FORMULA_START_BITS =
-            getBits(ATOMIC_FORMULA_START, SEPARATOR_BITS);
-    private static final int INVALID_FORMULA_SEPARATOR_VALUE = (1 << SEPARATOR_BITS) - 1;
-    private static final String INVALID_FORMULA_SEPARATOR_BITS =
-            getBits(INVALID_FORMULA_SEPARATOR_VALUE, SEPARATOR_BITS);
-
-    private static final String NOT = getBits(CompoundFormula.NOT, CONNECTOR_BITS);
-    private static final String AND = getBits(CompoundFormula.AND, CONNECTOR_BITS);
-    private static final String OR = getBits(CompoundFormula.OR, CONNECTOR_BITS);
-    private static final int INVALID_CONNECTOR_VALUE = 3;
-    private static final String INVALID_CONNECTOR =
-            getBits(INVALID_CONNECTOR_VALUE, CONNECTOR_BITS);
-
-    private static final String PACKAGE_NAME = getBits(AtomicFormula.PACKAGE_NAME, KEY_BITS);
-    private static final String APP_CERTIFICATE = getBits(AtomicFormula.APP_CERTIFICATE, KEY_BITS);
-    private static final String APP_CERTIFICATE_LINEAGE =
-            getBits(AtomicFormula.APP_CERTIFICATE_LINEAGE, KEY_BITS);
-    private static final String VERSION_CODE = getBits(AtomicFormula.VERSION_CODE, KEY_BITS);
-    private static final String PRE_INSTALLED = getBits(AtomicFormula.PRE_INSTALLED, KEY_BITS);
-    private static final int INVALID_KEY_VALUE = 9;
-    private static final String INVALID_KEY = getBits(INVALID_KEY_VALUE, KEY_BITS);
-
-    private static final String EQ = getBits(AtomicFormula.EQ, OPERATOR_BITS);
-    private static final int INVALID_OPERATOR_VALUE = 5;
-    private static final String INVALID_OPERATOR = getBits(INVALID_OPERATOR_VALUE, OPERATOR_BITS);
-
-    private static final String IS_NOT_HASHED = "0";
-    private static final String IS_HASHED = "1";
-
-    private static final String DENY = getBits(Rule.DENY, EFFECT_BITS);
-    private static final int INVALID_EFFECT_VALUE = 5;
-    private static final String INVALID_EFFECT = getBits(INVALID_EFFECT_VALUE, EFFECT_BITS);
-
-    private static final String START_BIT = "1";
-    private static final String END_BIT = "1";
-    private static final String INVALID_MARKER_BIT = "0";
-
-    private static final byte[] DEFAULT_FORMAT_VERSION_BYTES =
-            getBytes(getBits(DEFAULT_FORMAT_VERSION, FORMAT_VERSION_BITS));
-
-    private static final List<RuleIndexRange> NO_INDEXING = Collections.emptyList();
-
-    @Test
-    public void testBinaryStream_validCompoundFormula_noIndexing() throws Exception {
-        String packageName = "com.test.app";
-        String ruleBits =
-                START_BIT
-                        + COMPOUND_FORMULA_START_BITS
-                        + NOT
-                        + ATOMIC_FORMULA_START_BITS
-                        + PACKAGE_NAME
-                        + EQ
-                        + IS_NOT_HASHED
-                        + getBits(packageName.length(), VALUE_SIZE_BITS)
-                        + getValueBits(packageName)
-                        + COMPOUND_FORMULA_END_BITS
-                        + DENY
-                        + END_BIT;
-        byte[] ruleBytes = getBytes(ruleBits);
-        ByteBuffer rule =
-                ByteBuffer.allocate(DEFAULT_FORMAT_VERSION_BYTES.length + ruleBytes.length);
-        rule.put(DEFAULT_FORMAT_VERSION_BYTES);
-        rule.put(ruleBytes);
-        RuleParser binaryParser = new RuleBinaryParser();
-        Rule expectedRule =
-                new Rule(
-                        new CompoundFormula(
-                                CompoundFormula.NOT,
-                                Collections.singletonList(
-                                        new AtomicFormula.StringAtomicFormula(
-                                                AtomicFormula.PACKAGE_NAME,
-                                                packageName,
-                                                /* isHashedValue= */ false))),
-                        Rule.DENY);
-
-        List<Rule> rules =
-                binaryParser.parse(RandomAccessObject.ofBytes(rule.array()), NO_INDEXING);
-
-        assertThat(rules).isEqualTo(Collections.singletonList(expectedRule));
-    }
-
-    @Test
-    public void testBinaryString_validCompoundFormula_notConnector_noIndexing() throws Exception {
-        String packageName = "com.test.app";
-        String ruleBits =
-                START_BIT
-                        + COMPOUND_FORMULA_START_BITS
-                        + NOT
-                        + ATOMIC_FORMULA_START_BITS
-                        + PACKAGE_NAME
-                        + EQ
-                        + IS_NOT_HASHED
-                        + getBits(packageName.length(), VALUE_SIZE_BITS)
-                        + getValueBits(packageName)
-                        + COMPOUND_FORMULA_END_BITS
-                        + DENY
-                        + END_BIT;
-        byte[] ruleBytes = getBytes(ruleBits);
-        ByteBuffer rule =
-                ByteBuffer.allocate(DEFAULT_FORMAT_VERSION_BYTES.length + ruleBytes.length);
-        rule.put(DEFAULT_FORMAT_VERSION_BYTES);
-        rule.put(ruleBytes);
-        RuleParser binaryParser = new RuleBinaryParser();
-        Rule expectedRule =
-                new Rule(
-                        new CompoundFormula(
-                                CompoundFormula.NOT,
-                                Collections.singletonList(
-                                        new AtomicFormula.StringAtomicFormula(
-                                                AtomicFormula.PACKAGE_NAME,
-                                                packageName,
-                                                /* isHashedValue= */ false))),
-                        Rule.DENY);
-
-        List<Rule> rules = binaryParser.parse(rule.array());
-
-        assertThat(rules).isEqualTo(Collections.singletonList(expectedRule));
-    }
-
-    @Test
-    public void testBinaryString_validCompoundFormula_andConnector_noIndexing() throws Exception {
-        String packageName = "com.test.app";
-        String appCertificate = "test_cert";
-        String ruleBits =
-                START_BIT
-                        + COMPOUND_FORMULA_START_BITS
-                        + AND
-                        + ATOMIC_FORMULA_START_BITS
-                        + PACKAGE_NAME
-                        + EQ
-                        + IS_NOT_HASHED
-                        + getBits(packageName.length(), VALUE_SIZE_BITS)
-                        + getValueBits(packageName)
-                        + ATOMIC_FORMULA_START_BITS
-                        + APP_CERTIFICATE
-                        + EQ
-                        + IS_NOT_HASHED
-                        + getBits(appCertificate.length(), VALUE_SIZE_BITS)
-                        + getValueBits(appCertificate)
-                        + COMPOUND_FORMULA_END_BITS
-                        + DENY
-                        + END_BIT;
-        byte[] ruleBytes = getBytes(ruleBits);
-        ByteBuffer rule =
-                ByteBuffer.allocate(DEFAULT_FORMAT_VERSION_BYTES.length + ruleBytes.length);
-        rule.put(DEFAULT_FORMAT_VERSION_BYTES);
-        rule.put(ruleBytes);
-        RuleParser binaryParser = new RuleBinaryParser();
-        Rule expectedRule =
-                new Rule(
-                        new CompoundFormula(
-                                CompoundFormula.AND,
-                                Arrays.asList(
-                                        new AtomicFormula.StringAtomicFormula(
-                                                AtomicFormula.PACKAGE_NAME,
-                                                packageName,
-                                                /* isHashedValue= */ false),
-                                        new AtomicFormula.StringAtomicFormula(
-                                                AtomicFormula.APP_CERTIFICATE,
-                                                appCertificate,
-                                                /* isHashedValue= */ false))),
-                        Rule.DENY);
-        List<Rule> rules = binaryParser.parse(rule.array());
-
-        assertThat(rules).isEqualTo(Collections.singletonList(expectedRule));
-    }
-
-    @Test
-    public void testBinaryString_validCompoundFormula_orConnector_noIndexing() throws Exception {
-        String packageName = "com.test.app";
-        String appCertificate = "test_cert";
-        String ruleBits =
-                START_BIT
-                        + COMPOUND_FORMULA_START_BITS
-                        + OR
-                        + ATOMIC_FORMULA_START_BITS
-                        + PACKAGE_NAME
-                        + EQ
-                        + IS_NOT_HASHED
-                        + getBits(packageName.length(), VALUE_SIZE_BITS)
-                        + getValueBits(packageName)
-                        + ATOMIC_FORMULA_START_BITS
-                        + APP_CERTIFICATE
-                        + EQ
-                        + IS_NOT_HASHED
-                        + getBits(appCertificate.length(), VALUE_SIZE_BITS)
-                        + getValueBits(appCertificate)
-                        + COMPOUND_FORMULA_END_BITS
-                        + DENY
-                        + END_BIT;
-        byte[] ruleBytes = getBytes(ruleBits);
-        ByteBuffer rule =
-                ByteBuffer.allocate(DEFAULT_FORMAT_VERSION_BYTES.length + ruleBytes.length);
-        rule.put(DEFAULT_FORMAT_VERSION_BYTES);
-        rule.put(ruleBytes);
-        RuleParser binaryParser = new RuleBinaryParser();
-        Rule expectedRule =
-                new Rule(
-                        new CompoundFormula(
-                                CompoundFormula.OR,
-                                Arrays.asList(
-                                        new AtomicFormula.StringAtomicFormula(
-                                                AtomicFormula.PACKAGE_NAME,
-                                                packageName,
-                                                /* isHashedValue= */ false),
-                                        new AtomicFormula.StringAtomicFormula(
-                                                AtomicFormula.APP_CERTIFICATE,
-                                                appCertificate,
-                                                /* isHashedValue= */ false))),
-                        Rule.DENY);
-
-        List<Rule> rules = binaryParser.parse(rule.array());
-
-        assertThat(rules).isEqualTo(Collections.singletonList(expectedRule));
-    }
-
-    @Test
-    public void testBinaryString_validAtomicFormula_stringValue_noIndexing() throws Exception {
-        String packageName = "com.test.app";
-        String ruleBits =
-                START_BIT
-                        + ATOMIC_FORMULA_START_BITS
-                        + PACKAGE_NAME
-                        + EQ
-                        + IS_NOT_HASHED
-                        + getBits(packageName.length(), VALUE_SIZE_BITS)
-                        + getValueBits(packageName)
-                        + DENY
-                        + END_BIT;
-        byte[] ruleBytes = getBytes(ruleBits);
-        ByteBuffer rule =
-                ByteBuffer.allocate(DEFAULT_FORMAT_VERSION_BYTES.length + ruleBytes.length);
-        rule.put(DEFAULT_FORMAT_VERSION_BYTES);
-        rule.put(ruleBytes);
-        RuleParser binaryParser = new RuleBinaryParser();
-        Rule expectedRule =
-                new Rule(
-                        new AtomicFormula.StringAtomicFormula(
-                                AtomicFormula.PACKAGE_NAME,
-                                packageName,
-                                /* isHashedValue= */ false),
-                        Rule.DENY);
-
-        List<Rule> rules = binaryParser.parse(rule.array());
-
-        assertThat(rules).isEqualTo(Collections.singletonList(expectedRule));
-    }
-
-    @Test
-    public void testBinaryString_validAtomicFormula_hashedValue_noIndexing() throws Exception {
-        String appCertificate = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
-        String ruleBits =
-                START_BIT
-                        + ATOMIC_FORMULA_START_BITS
-                        + APP_CERTIFICATE
-                        + EQ
-                        + IS_HASHED
-                        + getBits(appCertificate.length(), VALUE_SIZE_BITS)
-                        + getValueBits(appCertificate)
-                        + DENY
-                        + END_BIT;
-        byte[] ruleBytes = getBytes(ruleBits);
-        ByteBuffer rule =
-                ByteBuffer.allocate(DEFAULT_FORMAT_VERSION_BYTES.length + ruleBytes.length);
-        rule.put(DEFAULT_FORMAT_VERSION_BYTES);
-        rule.put(ruleBytes);
-
-        RuleParser binaryParser = new RuleBinaryParser();
-        Rule expectedRule =
-                new Rule(
-                        new AtomicFormula.StringAtomicFormula(
-                                AtomicFormula.APP_CERTIFICATE,
-                                IntegrityUtils.getHexDigest(
-                                        appCertificate.getBytes(StandardCharsets.UTF_8)),
-                                /* isHashedValue= */ true),
-                        Rule.DENY);
-
-        List<Rule> rules = binaryParser.parse(rule.array());
-
-        assertThat(rules).isEqualTo(Collections.singletonList(expectedRule));
-    }
-
-    @Test
-    public void testBinaryString_validAtomicFormulaWithCertificateLineage() throws Exception {
-        String appCertificate = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
-        String ruleBits =
-                START_BIT
-                        + ATOMIC_FORMULA_START_BITS
-                        + APP_CERTIFICATE_LINEAGE
-                        + EQ
-                        + IS_HASHED
-                        + getBits(appCertificate.length(), VALUE_SIZE_BITS)
-                        + getValueBits(appCertificate)
-                        + DENY
-                        + END_BIT;
-        byte[] ruleBytes = getBytes(ruleBits);
-        ByteBuffer rule =
-                ByteBuffer.allocate(DEFAULT_FORMAT_VERSION_BYTES.length + ruleBytes.length);
-        rule.put(DEFAULT_FORMAT_VERSION_BYTES);
-        rule.put(ruleBytes);
-
-        RuleParser binaryParser = new RuleBinaryParser();
-        Rule expectedRule =
-                new Rule(
-                        new AtomicFormula.StringAtomicFormula(
-                                AtomicFormula.APP_CERTIFICATE_LINEAGE,
-                                IntegrityUtils.getHexDigest(
-                                        appCertificate.getBytes(StandardCharsets.UTF_8)),
-                                /* isHashedValue= */ true),
-                        Rule.DENY);
-
-        List<Rule> rules = binaryParser.parse(rule.array());
-
-        assertThat(rules).isEqualTo(Collections.singletonList(expectedRule));
-    }
-
-    @Test
-    public void testBinaryString_validAtomicFormula_integerValue_noIndexing() throws Exception {
-        int versionCode = 1;
-        String ruleBits =
-                START_BIT
-                        + ATOMIC_FORMULA_START_BITS
-                        + VERSION_CODE
-                        + EQ
-                        + getBits(versionCode, /* numOfBits= */ 64)
-                        + DENY
-                        + END_BIT;
-        byte[] ruleBytes = getBytes(ruleBits);
-        ByteBuffer rule =
-                ByteBuffer.allocate(DEFAULT_FORMAT_VERSION_BYTES.length + ruleBytes.length);
-        rule.put(DEFAULT_FORMAT_VERSION_BYTES);
-        rule.put(ruleBytes);
-        RuleParser binaryParser = new RuleBinaryParser();
-        Rule expectedRule =
-                new Rule(
-                        new AtomicFormula.LongAtomicFormula(
-                                AtomicFormula.VERSION_CODE, AtomicFormula.EQ, 1),
-                        Rule.DENY);
-
-        List<Rule> rules = binaryParser.parse(rule.array());
-
-        assertThat(rules).isEqualTo(Collections.singletonList(expectedRule));
-    }
-
-    @Test
-    public void testBinaryString_validAtomicFormula_booleanValue_noIndexing() throws Exception {
-        String isPreInstalled = "1";
-        String ruleBits =
-                START_BIT
-                        + ATOMIC_FORMULA_START_BITS
-                        + PRE_INSTALLED
-                        + EQ
-                        + isPreInstalled
-                        + DENY
-                        + END_BIT;
-        byte[] ruleBytes = getBytes(ruleBits);
-        ByteBuffer rule =
-                ByteBuffer.allocate(DEFAULT_FORMAT_VERSION_BYTES.length + ruleBytes.length);
-        rule.put(DEFAULT_FORMAT_VERSION_BYTES);
-        rule.put(ruleBytes);
-        RuleParser binaryParser = new RuleBinaryParser();
-        Rule expectedRule =
-                new Rule(
-                        new AtomicFormula.BooleanAtomicFormula(
-                                AtomicFormula.PRE_INSTALLED, true),
-                        Rule.DENY);
-
-        List<Rule> rules = binaryParser.parse(rule.array());
-
-        assertThat(rules).isEqualTo(Collections.singletonList(expectedRule));
-    }
-
-    @Test
-    public void testBinaryString_invalidAtomicFormula_noIndexing() {
-        int versionCode = 1;
-        String ruleBits =
-                START_BIT
-                        + ATOMIC_FORMULA_START_BITS
-                        + VERSION_CODE
-                        + EQ
-                        + getBits(versionCode, /* numOfBits= */ 64)
-                        + DENY;
-        byte[] ruleBytes = getBytes(ruleBits);
-        ByteBuffer rule =
-                ByteBuffer.allocate(DEFAULT_FORMAT_VERSION_BYTES.length + ruleBytes.length);
-        rule.put(DEFAULT_FORMAT_VERSION_BYTES);
-        rule.put(ruleBytes);
-        RuleParser binaryParser = new RuleBinaryParser();
-
-        assertExpectException(
-                RuleParseException.class,
-                /* expectedExceptionMessageRegex= */ "A rule must end with a '1' bit.",
-                () -> binaryParser.parse(rule.array()));
-    }
-
-    @Test
-    public void testBinaryString_withNoRuleList_noIndexing() throws RuleParseException {
-        ByteBuffer rule = ByteBuffer.allocate(DEFAULT_FORMAT_VERSION_BYTES.length);
-        rule.put(DEFAULT_FORMAT_VERSION_BYTES);
-        RuleParser binaryParser = new RuleBinaryParser();
-
-        List<Rule> rules = binaryParser.parse(rule.array());
-
-        assertThat(rules).isEmpty();
-    }
-
-    @Test
-    public void testBinaryString_withEmptyRule_noIndexing() {
-        String ruleBits = START_BIT;
-        byte[] ruleBytes = getBytes(ruleBits);
-        ByteBuffer rule =
-                ByteBuffer.allocate(DEFAULT_FORMAT_VERSION_BYTES.length + ruleBytes.length);
-        rule.put(DEFAULT_FORMAT_VERSION_BYTES);
-        rule.put(ruleBytes);
-        RuleParser binaryParser = new RuleBinaryParser();
-
-        assertExpectException(
-                RuleParseException.class,
-                /* expectedExceptionMessageRegex */ "",
-                () -> binaryParser.parse(rule.array()));
-    }
-
-    @Test
-    public void testBinaryString_invalidCompoundFormula_invalidNumberOfFormulas_noIndexing() {
-        String packageName = "com.test.app";
-        String appCertificate = "test_cert";
-        String ruleBits =
-                START_BIT
-                        + COMPOUND_FORMULA_START_BITS
-                        + NOT
-                        + ATOMIC_FORMULA_START_BITS
-                        + PACKAGE_NAME
-                        + EQ
-                        + IS_NOT_HASHED
-                        + getBits(packageName.length(), VALUE_SIZE_BITS)
-                        + getValueBits(packageName)
-                        + ATOMIC_FORMULA_START_BITS
-                        + APP_CERTIFICATE
-                        + EQ
-                        + IS_NOT_HASHED
-                        + getBits(appCertificate.length(), VALUE_SIZE_BITS)
-                        + getValueBits(appCertificate)
-                        + COMPOUND_FORMULA_END_BITS
-                        + DENY
-                        + END_BIT;
-        byte[] ruleBytes = getBytes(ruleBits);
-        ByteBuffer rule =
-                ByteBuffer.allocate(DEFAULT_FORMAT_VERSION_BYTES.length + ruleBytes.length);
-        rule.put(DEFAULT_FORMAT_VERSION_BYTES);
-        rule.put(ruleBytes);
-        RuleParser binaryParser = new RuleBinaryParser();
-
-        assertExpectException(
-                RuleParseException.class,
-                /* expectedExceptionMessageRegex */ "Connector NOT must have 1 formula only",
-                () -> binaryParser.parse(rule.array()));
-    }
-
-    @Test
-    public void testBinaryString_invalidRule_invalidOperator_noIndexing() {
-        int versionCode = 1;
-        String ruleBits =
-                START_BIT
-                        + COMPOUND_FORMULA_START_BITS
-                        + NOT
-                        + ATOMIC_FORMULA_START_BITS
-                        + VERSION_CODE
-                        + INVALID_OPERATOR
-                        + getBits(versionCode, /* numOfBits= */ 64)
-                        + COMPOUND_FORMULA_END_BITS
-                        + DENY
-                        + END_BIT;
-        byte[] ruleBytes = getBytes(ruleBits);
-        ByteBuffer rule =
-                ByteBuffer.allocate(DEFAULT_FORMAT_VERSION_BYTES.length + ruleBytes.length);
-        rule.put(DEFAULT_FORMAT_VERSION_BYTES);
-        rule.put(ruleBytes);
-        RuleParser binaryParser = new RuleBinaryParser();
-
-        assertExpectException(
-                RuleParseException.class,
-                /* expectedExceptionMessageRegex */ String.format(
-                        "Unknown operator: %d", INVALID_OPERATOR_VALUE),
-                () -> binaryParser.parse(rule.array()));
-    }
-
-    @Test
-    public void testBinaryString_invalidRule_invalidEffect_noIndexing() {
-        String packageName = "com.test.app";
-        String ruleBits =
-                START_BIT
-                        + COMPOUND_FORMULA_START_BITS
-                        + NOT
-                        + ATOMIC_FORMULA_START_BITS
-                        + PACKAGE_NAME
-                        + EQ
-                        + IS_NOT_HASHED
-                        + getBits(packageName.length(), VALUE_SIZE_BITS)
-                        + getValueBits(packageName)
-                        + COMPOUND_FORMULA_END_BITS
-                        + INVALID_EFFECT
-                        + END_BIT;
-        byte[] ruleBytes = getBytes(ruleBits);
-        ByteBuffer rule =
-                ByteBuffer.allocate(DEFAULT_FORMAT_VERSION_BYTES.length + ruleBytes.length);
-        rule.put(DEFAULT_FORMAT_VERSION_BYTES);
-        rule.put(ruleBytes);
-        RuleParser binaryParser = new RuleBinaryParser();
-
-        assertExpectException(
-                RuleParseException.class,
-                /* expectedExceptionMessageRegex */ String.format(
-                        "Unknown effect: %d", INVALID_EFFECT_VALUE),
-                () -> binaryParser.parse(rule.array()));
-    }
-
-    @Test
-    public void testBinaryString_invalidRule_invalidConnector_noIndexing() {
-        String packageName = "com.test.app";
-        String ruleBits =
-                START_BIT
-                        + COMPOUND_FORMULA_START_BITS
-                        + INVALID_CONNECTOR
-                        + ATOMIC_FORMULA_START_BITS
-                        + PACKAGE_NAME
-                        + EQ
-                        + IS_NOT_HASHED
-                        + getBits(packageName.length(), VALUE_SIZE_BITS)
-                        + getValueBits(packageName)
-                        + COMPOUND_FORMULA_END_BITS
-                        + DENY
-                        + END_BIT;
-        byte[] ruleBytes = getBytes(ruleBits);
-        ByteBuffer rule =
-                ByteBuffer.allocate(DEFAULT_FORMAT_VERSION_BYTES.length + ruleBytes.length);
-        rule.put(DEFAULT_FORMAT_VERSION_BYTES);
-        rule.put(ruleBytes);
-        RuleParser binaryParser = new RuleBinaryParser();
-
-        assertExpectException(
-                RuleParseException.class,
-                /* expectedExceptionMessageRegex */ String.format(
-                        "Unknown connector: %d", INVALID_CONNECTOR_VALUE),
-                () -> binaryParser.parse(rule.array()));
-    }
-
-    @Test
-    public void testBinaryString_invalidRule_invalidKey_noIndexing() {
-        String packageName = "com.test.app";
-        String ruleBits =
-                START_BIT
-                        + COMPOUND_FORMULA_START_BITS
-                        + NOT
-                        + ATOMIC_FORMULA_START_BITS
-                        + INVALID_KEY
-                        + EQ
-                        + IS_NOT_HASHED
-                        + getBits(packageName.length(), VALUE_SIZE_BITS)
-                        + getValueBits(packageName)
-                        + COMPOUND_FORMULA_END_BITS
-                        + DENY
-                        + END_BIT;
-        byte[] ruleBytes = getBytes(ruleBits);
-        ByteBuffer rule =
-                ByteBuffer.allocate(DEFAULT_FORMAT_VERSION_BYTES.length + ruleBytes.length);
-        rule.put(DEFAULT_FORMAT_VERSION_BYTES);
-        rule.put(ruleBytes);
-        RuleParser binaryParser = new RuleBinaryParser();
-
-        assertExpectException(
-                RuleParseException.class,
-                /* expectedExceptionMessageRegex */ String.format(
-                        "Unknown key: %d", INVALID_KEY_VALUE),
-                () -> binaryParser.parse(rule.array()));
-    }
-
-    @Test
-    public void testBinaryString_invalidRule_invalidSeparator_noIndexing() {
-        String packageName = "com.test.app";
-        String ruleBits =
-                START_BIT
-                        + INVALID_FORMULA_SEPARATOR_BITS
-                        + NOT
-                        + ATOMIC_FORMULA_START_BITS
-                        + PACKAGE_NAME
-                        + EQ
-                        + IS_NOT_HASHED
-                        + getBits(packageName.length(), VALUE_SIZE_BITS)
-                        + getValueBits(packageName)
-                        + COMPOUND_FORMULA_END_BITS
-                        + DENY
-                        + END_BIT;
-        byte[] ruleBytes = getBytes(ruleBits);
-        ByteBuffer rule =
-                ByteBuffer.allocate(DEFAULT_FORMAT_VERSION_BYTES.length + ruleBytes.length);
-        rule.put(DEFAULT_FORMAT_VERSION_BYTES);
-        rule.put(ruleBytes);
-        RuleParser binaryParser = new RuleBinaryParser();
-
-        assertExpectException(
-                RuleParseException.class,
-                /* expectedExceptionMessageRegex */ String.format(
-                        "Unknown formula separator: %d", INVALID_FORMULA_SEPARATOR_VALUE),
-                () -> binaryParser.parse(rule.array()));
-    }
-
-    @Test
-    public void testBinaryString_invalidRule_invalidEndMarker_noIndexing() {
-        String packageName = "com.test.app";
-        String ruleBits =
-                START_BIT
-                        + COMPOUND_FORMULA_START_BITS
-                        + NOT
-                        + ATOMIC_FORMULA_START_BITS
-                        + PACKAGE_NAME
-                        + EQ
-                        + IS_NOT_HASHED
-                        + getBits(packageName.length(), VALUE_SIZE_BITS)
-                        + getValueBits(packageName)
-                        + COMPOUND_FORMULA_END_BITS
-                        + DENY
-                        + INVALID_MARKER_BIT;
-        byte[] ruleBytes = getBytes(ruleBits);
-        ByteBuffer rule =
-                ByteBuffer.allocate(DEFAULT_FORMAT_VERSION_BYTES.length + ruleBytes.length);
-        rule.put(DEFAULT_FORMAT_VERSION_BYTES);
-        rule.put(ruleBytes);
-        RuleParser binaryParser = new RuleBinaryParser();
-
-        assertExpectException(
-                RuleParseException.class,
-                /* expectedExceptionMessageRegex */ "A rule must end with a '1' bit",
-                () -> binaryParser.parse(rule.array()));
-    }
-}
diff --git a/services/tests/servicestests/src/com/android/server/integrity/parser/RuleIndexingControllerTest.java b/services/tests/servicestests/src/com/android/server/integrity/parser/RuleIndexingControllerTest.java
deleted file mode 100644
index 370bd80..0000000
--- a/services/tests/servicestests/src/com/android/server/integrity/parser/RuleIndexingControllerTest.java
+++ /dev/null
@@ -1,216 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.integrity.parser;
-
-import static com.android.server.integrity.model.ComponentBitSize.VALUE_SIZE_BITS;
-import static com.android.server.integrity.model.IndexingFileConstants.END_INDEXING_KEY;
-import static com.android.server.integrity.model.IndexingFileConstants.START_INDEXING_KEY;
-import static com.android.server.integrity.utils.TestUtils.getBits;
-import static com.android.server.integrity.utils.TestUtils.getBytes;
-import static com.android.server.integrity.utils.TestUtils.getValueBits;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.testng.Assert.assertThrows;
-
-import android.content.integrity.AppInstallMetadata;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.nio.ByteBuffer;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-
-@RunWith(JUnit4.class)
-public class RuleIndexingControllerTest {
-
-    @Test
-    public void verifyIndexRangeSearchIsCorrect() throws IOException {
-        InputStream inputStream = obtainDefaultIndexingMapForTest();
-
-        RuleIndexingController indexingController = new RuleIndexingController(inputStream);
-
-        AppInstallMetadata appInstallMetadata =
-                new AppInstallMetadata.Builder()
-                        .setPackageName("ddd")
-                        .setAppCertificates(Collections.singletonList("777"))
-                        .setAppCertificateLineage(Collections.singletonList("777"))
-                        .build();
-
-        List<RuleIndexRange> resultingIndexes =
-                indexingController.identifyRulesToEvaluate(appInstallMetadata);
-
-        assertThat(resultingIndexes)
-                .containsExactly(
-                        new RuleIndexRange(200, 300),
-                        new RuleIndexRange(700, 800),
-                        new RuleIndexRange(900, 945));
-    }
-
-    @Test
-    public void verifyIndexRangeSearchIsCorrect_multipleAppCertificates() throws IOException {
-        InputStream inputStream = obtainDefaultIndexingMapForTest();
-
-        RuleIndexingController indexingController = new RuleIndexingController(inputStream);
-
-        AppInstallMetadata appInstallMetadata =
-                new AppInstallMetadata.Builder()
-                        .setPackageName("ddd")
-                        .setAppCertificates(Arrays.asList("777", "999"))
-                        .setAppCertificateLineage(Arrays.asList("777", "999"))
-                        .build();
-
-        List<RuleIndexRange> resultingIndexes =
-                indexingController.identifyRulesToEvaluate(appInstallMetadata);
-
-        assertThat(resultingIndexes)
-                .containsExactly(
-                        new RuleIndexRange(200, 300),
-                        new RuleIndexRange(700, 800),
-                        new RuleIndexRange(800, 900),
-                        new RuleIndexRange(900, 945));
-    }
-
-    @Test
-    public void verifyIndexRangeSearchIsCorrect_keysInFirstAndLastBlock() throws IOException {
-        InputStream inputStream = obtainDefaultIndexingMapForTest();
-
-        RuleIndexingController indexingController = new RuleIndexingController(inputStream);
-
-        AppInstallMetadata appInstallMetadata =
-                new AppInstallMetadata.Builder()
-                        .setPackageName("bbb")
-                        .setAppCertificates(Collections.singletonList("999"))
-                        .setAppCertificateLineage(Collections.singletonList("999"))
-                        .build();
-
-        List<RuleIndexRange> resultingIndexes =
-                indexingController.identifyRulesToEvaluate(appInstallMetadata);
-
-        assertThat(resultingIndexes)
-                .containsExactly(
-                        new RuleIndexRange(100, 200),
-                        new RuleIndexRange(800, 900),
-                        new RuleIndexRange(900, 945));
-    }
-
-    @Test
-    public void verifyIndexRangeSearchIsCorrect_keysMatchWithValues() throws IOException {
-        InputStream inputStream = obtainDefaultIndexingMapForTest();
-
-        RuleIndexingController indexingController = new RuleIndexingController(inputStream);
-
-        AppInstallMetadata appInstallMetadata =
-                new AppInstallMetadata.Builder()
-                        .setPackageName("ccc")
-                        .setAppCertificates(Collections.singletonList("444"))
-                        .setAppCertificateLineage(Collections.singletonList("444"))
-                        .build();
-
-        List<RuleIndexRange> resultingIndexes =
-                indexingController.identifyRulesToEvaluate(appInstallMetadata);
-
-        assertThat(resultingIndexes)
-                .containsExactly(
-                        new RuleIndexRange(200, 300),
-                        new RuleIndexRange(700, 800),
-                        new RuleIndexRange(900, 945));
-    }
-
-    @Test
-    public void verifyIndexRangeSearchIsCorrect_noIndexesAvailable() throws IOException {
-        byte[] stringBytes =
-                getBytes(
-                        getKeyValueString(START_INDEXING_KEY, 100)
-                                + getKeyValueString(END_INDEXING_KEY, 500)
-                                + getKeyValueString(START_INDEXING_KEY, 500)
-                                + getKeyValueString(END_INDEXING_KEY, 900)
-                                + getKeyValueString(START_INDEXING_KEY, 900)
-                                + getKeyValueString(END_INDEXING_KEY, 945));
-        ByteBuffer rule = ByteBuffer.allocate(stringBytes.length);
-        rule.put(stringBytes);
-        InputStream inputStream = new ByteArrayInputStream(rule.array());
-
-        RuleIndexingController indexingController = new RuleIndexingController(inputStream);
-
-        AppInstallMetadata appInstallMetadata =
-                new AppInstallMetadata.Builder()
-                        .setPackageName("ccc")
-                        .setAppCertificates(Collections.singletonList("444"))
-                        .setAppCertificateLineage(Collections.singletonList("444"))
-                        .build();
-
-        List<RuleIndexRange> resultingIndexes =
-                indexingController.identifyRulesToEvaluate(appInstallMetadata);
-
-        assertThat(resultingIndexes)
-                .containsExactly(
-                        new RuleIndexRange(100, 500),
-                        new RuleIndexRange(500, 900),
-                        new RuleIndexRange(900, 945));
-    }
-
-    @Test
-    public void verifyIndexingFileIsCorrupt() throws IOException {
-        byte[] stringBytes =
-                getBytes(
-                        getKeyValueString(START_INDEXING_KEY, 100)
-                                + getKeyValueString("ccc", 200)
-                                + getKeyValueString(END_INDEXING_KEY, 300)
-                                + getKeyValueString(END_INDEXING_KEY, 900));
-        ByteBuffer rule = ByteBuffer.allocate(stringBytes.length);
-        rule.put(stringBytes);
-        InputStream inputStream = new ByteArrayInputStream(rule.array());
-
-        assertThrows(IllegalStateException.class,
-                () -> new RuleIndexingController(inputStream));
-    }
-
-    private static InputStream obtainDefaultIndexingMapForTest() {
-        byte[] stringBytes =
-                getBytes(
-                        getKeyValueString(START_INDEXING_KEY, 100)
-                                + getKeyValueString("ccc", 200)
-                                + getKeyValueString("eee", 300)
-                                + getKeyValueString("hhh", 400)
-                                + getKeyValueString(END_INDEXING_KEY, 500)
-                                + getKeyValueString(START_INDEXING_KEY, 500)
-                                + getKeyValueString("111", 600)
-                                + getKeyValueString("444", 700)
-                                + getKeyValueString("888", 800)
-                                + getKeyValueString(END_INDEXING_KEY, 900)
-                                + getKeyValueString(START_INDEXING_KEY, 900)
-                                + getKeyValueString(END_INDEXING_KEY, 945));
-        ByteBuffer rule = ByteBuffer.allocate(stringBytes.length);
-        rule.put(stringBytes);
-        return new ByteArrayInputStream(rule.array());
-    }
-
-    private static String getKeyValueString(String key, int value) {
-        String isNotHashed = "0";
-        return isNotHashed
-                + getBits(key.length(), VALUE_SIZE_BITS)
-                + getValueBits(key)
-                + getBits(value, /* numOfBits= */ 32);
-    }
-}
diff --git a/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java
index b1d658c..73aec63 100644
--- a/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java
@@ -85,6 +85,7 @@
 import android.testing.TestableContext;
 import android.view.ContentRecordingSession;
 import android.view.ContentRecordingSession.RecordContent;
+import android.view.Display;
 
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.FlakyTest;
@@ -348,30 +349,42 @@
             .Flags.FLAG_MEDIA_PROJECTION_KEYGUARD_RESTRICTIONS)
     @Test
     public void testCreateProjection_keyguardLocked_RoleHeld() {
-        runWithRole(AssociationRequest.DEVICE_PROFILE_APP_STREAMING, () -> {
-            try {
-                mAppInfo.privateFlags |= PRIVATE_FLAG_PRIVILEGED;
-                doReturn(mAppInfo).when(mPackageManager).getApplicationInfoAsUser(anyString(),
-                        any(ApplicationInfoFlags.class), any(UserHandle.class));
-                MediaProjectionManagerService.MediaProjection projection =
-                        mService.createProjectionInternal(Process.myUid(),
-                                mContext.getPackageName(),
-                                TYPE_MIRRORING, /* isPermanentGrant= */ false, UserHandle.CURRENT);
-                doReturn(true).when(mKeyguardManager).isKeyguardLocked();
-                doReturn(PackageManager.PERMISSION_DENIED).when(
-                        mPackageManager).checkPermission(
-                        RECORD_SENSITIVE_CONTENT, projection.packageName);
+        runWithRole(
+                AssociationRequest.DEVICE_PROFILE_APP_STREAMING,
+                () -> {
+                    try {
+                        mAppInfo.privateFlags |= PRIVATE_FLAG_PRIVILEGED;
+                        doReturn(mAppInfo)
+                                .when(mPackageManager)
+                                .getApplicationInfoAsUser(
+                                        anyString(),
+                                        any(ApplicationInfoFlags.class),
+                                        any(UserHandle.class));
+                        MediaProjectionManagerService.MediaProjection projection =
+                                mService.createProjectionInternal(
+                                        Process.myUid(),
+                                        mContext.getPackageName(),
+                                        TYPE_MIRRORING,
+                                        /* isPermanentGrant= */ false,
+                                        UserHandle.CURRENT,
+                                        DEFAULT_DISPLAY);
+                        doReturn(true).when(mKeyguardManager).isKeyguardLocked();
+                        doReturn(PackageManager.PERMISSION_DENIED)
+                                .when(mPackageManager)
+                                .checkPermission(RECORD_SENSITIVE_CONTENT, projection.packageName);
 
-                projection.start(mIMediaProjectionCallback);
-                projection.notifyVirtualDisplayCreated(10);
+                        projection.start(mIMediaProjectionCallback);
+                        projection.notifyVirtualDisplayCreated(10);
 
-                // The projection was started because it was allowed to capture the keyguard.
-                assertWithMessage("Failed to run projection")
-                        .that(mService.getActiveProjectionInfo()).isNotNull();
-            } catch (NameNotFoundException e) {
-                throw new RuntimeException(e);
-            }
-        });
+                        // The projection was started because it was allowed to capture the
+                        // keyguard.
+                        assertWithMessage("Failed to run projection")
+                                .that(mService.getActiveProjectionInfo())
+                                .isNotNull();
+                    } catch (NameNotFoundException e) {
+                        throw new RuntimeException(e);
+                    }
+                });
     }
 
     @EnableFlags(android.companion.virtualdevice.flags
@@ -480,8 +493,13 @@
 
         // We are allowed to create another projection.
         MediaProjectionManagerService.MediaProjection secondProjection =
-                mService.createProjectionInternal(UID + 10, PACKAGE_NAME + "foo",
-                        TYPE_MIRRORING, /* isPermanentGrant= */ true, UserHandle.CURRENT);
+                mService.createProjectionInternal(
+                        UID + 10,
+                        PACKAGE_NAME + "foo",
+                        TYPE_MIRRORING,
+                        /* isPermanentGrant= */ true,
+                        UserHandle.CURRENT,
+                        Display.DEFAULT_DISPLAY);
 
         assertThat(secondProjection).isNotNull();
 
@@ -1246,6 +1264,13 @@
         verify(mWatcherCallback, never()).onRecordingSessionSet(any(), any());
     }
 
+    @Test
+    public void createProjectionForSecondaryDisplay() throws NameNotFoundException {
+        MediaProjectionManagerService.MediaProjection projection =
+                createProjectionPreconditions(mService, 200);
+        assertThat(projection.getDisplayId()).isEqualTo(200);
+    }
+
     private void verifySetSessionWithContent(@RecordContent int content) {
         verify(mWindowManagerInternal, atLeastOnce()).setContentRecordingSession(
                 mSessionCaptor.capture());
@@ -1255,12 +1280,21 @@
 
     // Set up preconditions for creating a projection.
     private MediaProjectionManagerService.MediaProjection createProjectionPreconditions(
-            MediaProjectionManagerService service)
-            throws NameNotFoundException {
+            MediaProjectionManagerService service) throws NameNotFoundException {
+        return createProjectionPreconditions(service, Display.DEFAULT_DISPLAY);
+    }
+
+    private MediaProjectionManagerService.MediaProjection createProjectionPreconditions(
+            MediaProjectionManagerService service, int displayId) throws NameNotFoundException {
         doReturn(mAppInfo).when(mPackageManager).getApplicationInfoAsUser(anyString(),
                 any(ApplicationInfoFlags.class), any(UserHandle.class));
-        return service.createProjectionInternal(UID, PACKAGE_NAME,
-                TYPE_MIRRORING, /* isPermanentGrant= */ false, UserHandle.CURRENT);
+        return service.createProjectionInternal(
+                UID,
+                PACKAGE_NAME,
+                TYPE_MIRRORING,
+                /* isPermanentGrant= */ false,
+                UserHandle.CURRENT,
+                displayId);
     }
 
     // Set up preconditions for starting a projection, with no foreground service requirements.
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserPropertiesTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserPropertiesTest.java
index 1331ae1..b110ff6 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserPropertiesTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserPropertiesTest.java
@@ -160,6 +160,7 @@
 
             // Make a possibly-not-full-permission (i.e. partial) copy and check that it is correct.
             final UserProperties copy = new UserProperties(orig, exposeAll, hasManage, hasQuery);
+            assertThat(copy.toString()).isNotNull();
             verifyTestCopyLacksPermissions(orig, copy, exposeAll, hasManage, hasQuery);
             if (permLevel < 1) {
                 // PropertiesPresent should definitely be different since not all items were copied.
diff --git a/services/tests/servicestests/src/com/android/server/security/advancedprotection/AdvancedProtectionServiceTest.java b/services/tests/servicestests/src/com/android/server/security/advancedprotection/AdvancedProtectionServiceTest.java
index 727b435..e97b48c 100644
--- a/services/tests/servicestests/src/com/android/server/security/advancedprotection/AdvancedProtectionServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/security/advancedprotection/AdvancedProtectionServiceTest.java
@@ -28,58 +28,188 @@
 import android.os.test.FakePermissionEnforcer;
 import android.os.test.TestLooper;
 import android.provider.Settings;
+import android.security.advancedprotection.IAdvancedProtectionCallback;
+
+import com.android.server.security.advancedprotection.features.AdvancedProtectionHook;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
 
+import java.util.concurrent.atomic.AtomicBoolean;
+
+@SuppressLint("VisibleForTests")
 @RunWith(JUnit4.class)
 public class AdvancedProtectionServiceTest {
     private AdvancedProtectionService mService;
     private FakePermissionEnforcer mPermissionEnforcer;
     private Context mContext;
+    private AdvancedProtectionService.AdvancedProtectionStore mStore;
+    private TestLooper mLooper;
 
     @Before
-    @SuppressLint("VisibleForTests")
     public void setup() throws Settings.SettingNotFoundException {
         mContext = mock(Context.class);
         mPermissionEnforcer = new FakePermissionEnforcer();
         mPermissionEnforcer.grant(Manifest.permission.SET_ADVANCED_PROTECTION_MODE);
         mPermissionEnforcer.grant(Manifest.permission.QUERY_ADVANCED_PROTECTION_MODE);
 
-        AdvancedProtectionService.AdvancedProtectionStore store =
-                new AdvancedProtectionService.AdvancedProtectionStore(mContext) {
-                    private boolean mEnabled = false;
+        mStore = new AdvancedProtectionService.AdvancedProtectionStore(mContext) {
+            private boolean mEnabled = false;
 
-                    @Override
-                    boolean retrieve() {
-                        return mEnabled;
-                    }
+            @Override
+            boolean retrieve() {
+                return mEnabled;
+            }
 
-                    @Override
-                    void store(boolean enabled) {
-                        this.mEnabled = enabled;
-                    }
-                };
+            @Override
+            void store(boolean enabled) {
+                this.mEnabled = enabled;
+            }
+        };
 
-        mService = new AdvancedProtectionService(mContext, store, new TestLooper().getLooper(),
-                mPermissionEnforcer);
+        mLooper = new TestLooper();
+        mService = new AdvancedProtectionService(mContext, mStore, mLooper.getLooper(),
+                mPermissionEnforcer, null);
     }
 
     @Test
-    public void testEnableProtection() throws RemoteException {
+    public void testToggleProtection() {
         mService.setAdvancedProtectionEnabled(true);
         assertTrue(mService.isAdvancedProtectionEnabled());
-    }
 
-    @Test
-    public void testDisableProtection() throws RemoteException {
         mService.setAdvancedProtectionEnabled(false);
         assertFalse(mService.isAdvancedProtectionEnabled());
     }
 
     @Test
+    public void testDisableProtection_byDefault() {
+        assertFalse(mService.isAdvancedProtectionEnabled());
+    }
+
+    @Test
+    public void testEnableProtection_withHook() {
+        AtomicBoolean callbackCaptor = new AtomicBoolean(false);
+        AdvancedProtectionHook hook =
+                new AdvancedProtectionHook(mContext, true) {
+                    @Override
+                    public boolean isAvailable() {
+                        return true;
+                    }
+
+                    @Override
+                    public void onAdvancedProtectionChanged(boolean enabled) {
+                        callbackCaptor.set(enabled);
+                    }
+                };
+
+        mService = new AdvancedProtectionService(mContext, mStore, mLooper.getLooper(),
+                mPermissionEnforcer, hook);
+        mService.setAdvancedProtectionEnabled(true);
+        mLooper.dispatchNext();
+
+        assertTrue(callbackCaptor.get());
+    }
+
+    @Test
+    public void testEnableProtection_withFeature_notAvailable() {
+        AtomicBoolean callbackCalledCaptor = new AtomicBoolean(false);
+        AdvancedProtectionHook hook =
+                new AdvancedProtectionHook(mContext, true) {
+                    @Override
+                    public boolean isAvailable() {
+                        return false;
+                    }
+
+                    @Override
+                    public void onAdvancedProtectionChanged(boolean enabled) {
+                        callbackCalledCaptor.set(true);
+                    }
+                };
+
+        mService = new AdvancedProtectionService(mContext, mStore, mLooper.getLooper(),
+                mPermissionEnforcer, hook);
+        mService.setAdvancedProtectionEnabled(true);
+        mLooper.dispatchNext();
+        assertFalse(callbackCalledCaptor.get());
+    }
+
+    @Test
+    public void testEnableProtection_withFeature_notCalledIfModeNotChanged() {
+        AtomicBoolean callbackCalledCaptor = new AtomicBoolean(false);
+        AdvancedProtectionHook hook =
+                new AdvancedProtectionHook(mContext, true) {
+                    @Override
+                    public boolean isAvailable() {
+                        return true;
+                    }
+
+                    @Override
+                    public void onAdvancedProtectionChanged(boolean enabled) {
+                        callbackCalledCaptor.set(true);
+                    }
+                };
+
+        mService = new AdvancedProtectionService(mContext, mStore, mLooper.getLooper(),
+                mPermissionEnforcer, hook);
+        mService.setAdvancedProtectionEnabled(true);
+        mLooper.dispatchNext();
+        assertTrue(callbackCalledCaptor.get());
+
+        callbackCalledCaptor.set(false);
+        mService.setAdvancedProtectionEnabled(true);
+        mLooper.dispatchAll();
+        assertFalse(callbackCalledCaptor.get());
+    }
+
+    @Test
+    public void testRegisterCallback() throws RemoteException {
+        AtomicBoolean callbackCaptor = new AtomicBoolean(false);
+        IAdvancedProtectionCallback callback = new IAdvancedProtectionCallback.Stub() {
+            @Override
+            public void onAdvancedProtectionChanged(boolean enabled) {
+                callbackCaptor.set(enabled);
+            }
+        };
+
+        mService.setAdvancedProtectionEnabled(true);
+        mLooper.dispatchAll();
+
+        mService.registerAdvancedProtectionCallback(callback);
+        mLooper.dispatchNext();
+        assertTrue(callbackCaptor.get());
+
+        mService.setAdvancedProtectionEnabled(false);
+        mLooper.dispatchNext();
+
+        assertFalse(callbackCaptor.get());
+    }
+
+    @Test
+    public void testUnregisterCallback() throws RemoteException {
+        AtomicBoolean callbackCalledCaptor = new AtomicBoolean(false);
+        IAdvancedProtectionCallback callback = new IAdvancedProtectionCallback.Stub() {
+            @Override
+            public void onAdvancedProtectionChanged(boolean enabled) {
+                callbackCalledCaptor.set(true);
+            }
+        };
+
+        mService.setAdvancedProtectionEnabled(true);
+        mService.registerAdvancedProtectionCallback(callback);
+        mLooper.dispatchAll();
+        callbackCalledCaptor.set(false);
+
+        mService.unregisterAdvancedProtectionCallback(callback);
+        mService.setAdvancedProtectionEnabled(false);
+
+        mLooper.dispatchNext();
+        assertFalse(callbackCalledCaptor.get());
+    }
+
+
+    @Test
     public void testSetProtection_withoutPermission() {
         mPermissionEnforcer.revoke(Manifest.permission.SET_ADVANCED_PROTECTION_MODE);
         assertThrows(SecurityException.class, () -> mService.setAdvancedProtectionEnabled(true));
@@ -90,4 +220,18 @@
         mPermissionEnforcer.revoke(Manifest.permission.QUERY_ADVANCED_PROTECTION_MODE);
         assertThrows(SecurityException.class, () -> mService.isAdvancedProtectionEnabled());
     }
+
+    @Test
+    public void testRegisterCallback_withoutPermission() {
+        mPermissionEnforcer.revoke(Manifest.permission.QUERY_ADVANCED_PROTECTION_MODE);
+        assertThrows(SecurityException.class, () -> mService.registerAdvancedProtectionCallback(
+                new IAdvancedProtectionCallback.Default()));
+    }
+
+    @Test
+    public void testUnregisterCallback_withoutPermission() {
+        mPermissionEnforcer.revoke(Manifest.permission.QUERY_ADVANCED_PROTECTION_MODE);
+        assertThrows(SecurityException.class, () -> mService.unregisterAdvancedProtectionCallback(
+                new IAdvancedProtectionCallback.Default()));
+    }
 }
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 e845d80..f60ec99 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -45,6 +45,9 @@
 import static android.app.Notification.VISIBILITY_PRIVATE;
 import static android.app.NotificationChannel.NEWS_ID;
 import static android.app.NotificationChannel.DEFAULT_CHANNEL_ID;
+import static android.app.NotificationChannel.PROMOTIONS_ID;
+import static android.app.NotificationChannel.RECS_ID;
+import static android.app.NotificationChannel.SOCIAL_MEDIA_ID;
 import static android.app.NotificationChannel.USER_LOCKED_ALLOW_BUBBLE;
 import static android.app.NotificationManager.ACTION_INTERRUPTION_FILTER_CHANGED;
 import static android.app.NotificationManager.BUBBLE_PREFERENCE_ALL;
@@ -98,7 +101,10 @@
 import static android.service.notification.Adjustment.KEY_TEXT_REPLIES;
 import static android.service.notification.Adjustment.KEY_TYPE;
 import static android.service.notification.Adjustment.KEY_USER_SENTIMENT;
+import static android.service.notification.Adjustment.TYPE_CONTENT_RECOMMENDATION;
 import static android.service.notification.Adjustment.TYPE_NEWS;
+import static android.service.notification.Adjustment.TYPE_PROMOTION;
+import static android.service.notification.Adjustment.TYPE_SOCIAL_MEDIA;
 import static android.service.notification.Condition.SOURCE_CONTEXT;
 import static android.service.notification.Condition.SOURCE_USER_ACTION;
 import static android.service.notification.Condition.STATE_TRUE;
@@ -7656,7 +7662,7 @@
         mService.mZenModeHelper = mZenModeHelper;
         NotificationManager.Policy userPolicy =
                 new NotificationManager.Policy(0, 0, 0, SUPPRESSED_EFFECT_BADGE);
-        when(mZenModeHelper.getNotificationPolicy()).thenReturn(userPolicy);
+        when(mZenModeHelper.getNotificationPolicy(any())).thenReturn(userPolicy);
 
         NotificationManager.Policy appPolicy = new NotificationManager.Policy(0, 0, 0,
                 SUPPRESSED_EFFECT_SCREEN_ON | SUPPRESSED_EFFECT_SCREEN_OFF);
@@ -7676,7 +7682,7 @@
         mService.mZenModeHelper = mZenModeHelper;
         NotificationManager.Policy userPolicy =
                 new NotificationManager.Policy(0, 0, 0, SUPPRESSED_EFFECT_BADGE);
-        when(mZenModeHelper.getNotificationPolicy()).thenReturn(userPolicy);
+        when(mZenModeHelper.getNotificationPolicy(any())).thenReturn(userPolicy);
 
         NotificationManager.Policy appPolicy = new NotificationManager.Policy(0, 0, 0,
                 SUPPRESSED_EFFECT_NOTIFICATION_LIST);
@@ -7693,7 +7699,7 @@
         mService.mZenModeHelper = mZenModeHelper;
         NotificationManager.Policy userPolicy =
                 new NotificationManager.Policy(0, 0, 0, SUPPRESSED_EFFECT_BADGE);
-        when(mZenModeHelper.getNotificationPolicy()).thenReturn(userPolicy);
+        when(mZenModeHelper.getNotificationPolicy(any())).thenReturn(userPolicy);
 
         NotificationManager.Policy appPolicy = new NotificationManager.Policy(0, 0, 0,
                 SUPPRESSED_EFFECT_SCREEN_ON | SUPPRESSED_EFFECT_STATUS_BAR);
@@ -7711,7 +7717,7 @@
         mService.mZenModeHelper = mZenModeHelper;
         NotificationManager.Policy userPolicy =
                 new NotificationManager.Policy(0, 0, 0, SUPPRESSED_EFFECT_BADGE);
-        when(mZenModeHelper.getNotificationPolicy()).thenReturn(userPolicy);
+        when(mZenModeHelper.getNotificationPolicy(any())).thenReturn(userPolicy);
 
         NotificationManager.Policy appPolicy = new NotificationManager.Policy(0, 0, 0,
                 SUPPRESSED_EFFECT_SCREEN_ON | SUPPRESSED_EFFECT_SCREEN_OFF);
@@ -7730,7 +7736,7 @@
         mService.mZenModeHelper = mZenModeHelper;
         NotificationManager.Policy userPolicy =
                 new NotificationManager.Policy(0, 0, 0, SUPPRESSED_EFFECT_BADGE);
-        when(mZenModeHelper.getNotificationPolicy()).thenReturn(userPolicy);
+        when(mZenModeHelper.getNotificationPolicy(any())).thenReturn(userPolicy);
 
         NotificationManager.Policy appPolicy = new NotificationManager.Policy(0, 0, 0,
                 SUPPRESSED_EFFECT_NOTIFICATION_LIST | SUPPRESSED_EFFECT_AMBIENT
@@ -7750,7 +7756,7 @@
         mService.mZenModeHelper = mZenModeHelper;
         NotificationManager.Policy userPolicy =
                 new NotificationManager.Policy(0, 0, 0, SUPPRESSED_EFFECT_BADGE);
-        when(mZenModeHelper.getNotificationPolicy()).thenReturn(userPolicy);
+        when(mZenModeHelper.getNotificationPolicy(any())).thenReturn(userPolicy);
 
         NotificationManager.Policy appPolicy = new NotificationManager.Policy(0, 0, 0,
                 SUPPRESSED_EFFECT_SCREEN_ON | SUPPRESSED_EFFECT_STATUS_BAR);
@@ -10392,7 +10398,7 @@
         mBinderService.addAutomaticZenRule(rule, "com.android.settings", false);
 
         // verify that zen mode helper gets passed in a package name of "android"
-        verify(mockZenModeHelper).addAutomaticZenRule(eq("android"), eq(rule),
+        verify(mockZenModeHelper).addAutomaticZenRule(any(), eq("android"), eq(rule),
                 eq(ZenModeConfig.ORIGIN_SYSTEM), anyString(), anyInt());
     }
 
@@ -10414,7 +10420,7 @@
         mBinderService.addAutomaticZenRule(rule, "com.android.settings", false);
 
         // verify that zen mode helper gets passed in a package name of "android"
-        verify(mockZenModeHelper).addAutomaticZenRule(eq("android"), eq(rule),
+        verify(mockZenModeHelper).addAutomaticZenRule(any(), eq("android"), eq(rule),
                 eq(ZenModeConfig.ORIGIN_SYSTEM), anyString(), anyInt());
     }
 
@@ -10434,9 +10440,9 @@
         mBinderService.addAutomaticZenRule(rule, "another.package", false);
 
         // verify that zen mode helper gets passed in the package name from the arg, not the owner
-        verify(mockZenModeHelper).addAutomaticZenRule(
-                eq("another.package"), eq(rule), eq(ZenModeConfig.ORIGIN_APP),
-                anyString(), anyInt());  // doesn't count as a system/systemui call
+        verify(mockZenModeHelper).addAutomaticZenRule(any(), eq("another.package"), eq(rule),
+                eq(ZenModeConfig.ORIGIN_APP), anyString(),
+                anyInt());  // doesn't count as a system/systemui call
     }
 
     @Test
@@ -10453,7 +10459,8 @@
 
         mBinderService.addAutomaticZenRule(rule, mPkg, /* fromUser= */ false);
 
-        verify(zenModeHelper).addAutomaticZenRule(eq(mPkg), eq(rule), anyInt(), any(), anyInt());
+        verify(zenModeHelper).addAutomaticZenRule(any(), eq(mPkg), eq(rule), anyInt(), any(),
+                anyInt());
     }
 
     @Test
@@ -10488,7 +10495,8 @@
 
         mBinderService.addAutomaticZenRule(rule, mPkg, /* fromUser= */ false);
 
-        verify(zenModeHelper).addAutomaticZenRule(eq(mPkg), eq(rule), anyInt(), any(), anyInt());
+        verify(zenModeHelper).addAutomaticZenRule(any(), eq(mPkg), eq(rule), anyInt(), any(),
+                anyInt());
     }
 
     @Test
@@ -10521,7 +10529,8 @@
 
         mBinderService.addAutomaticZenRule(rule, mPkg, /* fromUser= */ false);
 
-        verify(zenModeHelper).addAutomaticZenRule(eq(mPkg), eq(rule), anyInt(), any(), anyInt());
+        verify(zenModeHelper).addAutomaticZenRule(any(), eq(mPkg), eq(rule), anyInt(), any(),
+                anyInt());
     }
 
     private void addAutomaticZenRule_restrictedRuleTypeCannotBeUsedByRegularApps(
@@ -10549,7 +10558,7 @@
 
         mBinderService.addAutomaticZenRule(SOME_ZEN_RULE, "pkg", /* fromUser= */ true);
 
-        verify(zenModeHelper).addAutomaticZenRule(eq("pkg"), eq(SOME_ZEN_RULE),
+        verify(zenModeHelper).addAutomaticZenRule(any(), eq("pkg"), eq(SOME_ZEN_RULE),
                 eq(ZenModeConfig.ORIGIN_USER_IN_SYSTEMUI), anyString(), anyInt());
     }
 
@@ -10561,7 +10570,7 @@
 
         mBinderService.addAutomaticZenRule(SOME_ZEN_RULE, "pkg", /* fromUser= */ false);
 
-        verify(zenModeHelper).addAutomaticZenRule(eq("pkg"), eq(SOME_ZEN_RULE),
+        verify(zenModeHelper).addAutomaticZenRule(any(), eq("pkg"), eq(SOME_ZEN_RULE),
                 eq(ZenModeConfig.ORIGIN_SYSTEM), anyString(), anyInt());
     }
 
@@ -10573,7 +10582,7 @@
 
         mBinderService.addAutomaticZenRule(SOME_ZEN_RULE, "pkg", /* fromUser= */ false);
 
-        verify(zenModeHelper).addAutomaticZenRule(eq("pkg"), eq(SOME_ZEN_RULE),
+        verify(zenModeHelper).addAutomaticZenRule(any(), eq("pkg"), eq(SOME_ZEN_RULE),
                 eq(ZenModeConfig.ORIGIN_APP), anyString(), anyInt());
     }
 
@@ -10595,7 +10604,7 @@
 
         mBinderService.updateAutomaticZenRule("id", SOME_ZEN_RULE, /* fromUser= */ true);
 
-        verify(zenModeHelper).updateAutomaticZenRule(eq("id"), eq(SOME_ZEN_RULE),
+        verify(zenModeHelper).updateAutomaticZenRule(any(), eq("id"), eq(SOME_ZEN_RULE),
                 eq(ZenModeConfig.ORIGIN_USER_IN_SYSTEMUI), anyString(), anyInt());
     }
 
@@ -10617,7 +10626,7 @@
 
         mBinderService.removeAutomaticZenRule("id", /* fromUser= */ true);
 
-        verify(zenModeHelper).removeAutomaticZenRule(eq("id"),
+        verify(zenModeHelper).removeAutomaticZenRule(any(), eq("id"),
                 eq(ZenModeConfig.ORIGIN_USER_IN_SYSTEMUI), anyString(), anyInt());
     }
 
@@ -10642,7 +10651,7 @@
                 SOURCE_USER_ACTION);
         mBinderService.setAutomaticZenRuleState("id", withSourceUser);
 
-        verify(zenModeHelper).setAutomaticZenRuleState(eq("id"), eq(withSourceUser),
+        verify(zenModeHelper).setAutomaticZenRuleState(any(), eq("id"), eq(withSourceUser),
                 eq(ZenModeConfig.ORIGIN_USER_IN_APP), anyInt());
     }
 
@@ -10657,7 +10666,7 @@
                 SOURCE_CONTEXT);
         mBinderService.setAutomaticZenRuleState("id", withSourceContext);
 
-        verify(zenModeHelper).setAutomaticZenRuleState(eq("id"), eq(withSourceContext),
+        verify(zenModeHelper).setAutomaticZenRuleState(any(), eq("id"), eq(withSourceContext),
                 eq(ZenModeConfig.ORIGIN_APP), anyInt());
     }
 
@@ -10672,7 +10681,7 @@
                 SOURCE_USER_ACTION);
         mBinderService.setAutomaticZenRuleState("id", withSourceContext);
 
-        verify(zenModeHelper).setAutomaticZenRuleState(eq("id"), eq(withSourceContext),
+        verify(zenModeHelper).setAutomaticZenRuleState(any(), eq("id"), eq(withSourceContext),
                 eq(ZenModeConfig.ORIGIN_USER_IN_SYSTEMUI), anyInt());
     }
     @Test
@@ -10686,10 +10695,35 @@
                 SOURCE_CONTEXT);
         mBinderService.setAutomaticZenRuleState("id", withSourceContext);
 
-        verify(zenModeHelper).setAutomaticZenRuleState(eq("id"), eq(withSourceContext),
+        verify(zenModeHelper).setAutomaticZenRuleState(any(), eq("id"), eq(withSourceContext),
                 eq(ZenModeConfig.ORIGIN_SYSTEM), anyInt());
     }
 
+
+    @Test
+    @EnableFlags(android.app.Flags.FLAG_MODES_MULTIUSER)
+    public void getAutomaticZenRules_fromSystem_readsWithCurrentUser() throws Exception {
+        ZenModeHelper zenModeHelper = setUpMockZenTest();
+        mService.isSystemUid = true;
+
+        // Representative used to verify getCallingZenUser().
+        mBinderService.getAutomaticZenRules();
+
+        verify(zenModeHelper).getAutomaticZenRules(eq(UserHandle.CURRENT));
+    }
+
+    @Test
+    @EnableFlags(android.app.Flags.FLAG_MODES_MULTIUSER)
+    public void getAutomaticZenRules_fromNormalPackage_readsWithBinderUser() throws Exception {
+        ZenModeHelper zenModeHelper = setUpMockZenTest();
+        mService.setCallerIsNormalPackage();
+
+        // Representative used to verify getCallingZenUser().
+        mBinderService.getAutomaticZenRules();
+
+        verify(zenModeHelper).getAutomaticZenRules(eq(Binder.getCallingUserHandle()));
+    }
+
     /** Prepares for a zen-related test that uses a mocked {@link ZenModeHelper}. */
     private ZenModeHelper setUpMockZenTest() {
         ZenModeHelper zenModeHelper = mock(ZenModeHelper.class);
@@ -15809,7 +15843,8 @@
         NotificationManager.Policy policy = new NotificationManager.Policy(0, 0, 0);
         mBinderService.setNotificationPolicy("package", policy, false);
 
-        verify(zenHelper).applyGlobalPolicyAsImplicitZenRule(eq("package"), anyInt(), eq(policy));
+        verify(zenHelper).applyGlobalPolicyAsImplicitZenRule(any(), eq("package"), anyInt(),
+                eq(policy));
     }
 
     @Test
@@ -15825,7 +15860,7 @@
         NotificationManager.Policy policy = new NotificationManager.Policy(0, 0, 0);
         mBinderService.setNotificationPolicy("package", policy, false);
 
-        verify(zenModeHelper).setNotificationPolicy(eq(policy), anyInt(), anyInt());
+        verify(zenModeHelper).setNotificationPolicy(any(), eq(policy), anyInt(), anyInt());
     }
 
     @Test
@@ -15872,9 +15907,9 @@
         mBinderService.setNotificationPolicy("package", policy, false);
 
         if (canSetGlobalPolicy) {
-            verify(zenModeHelper).setNotificationPolicy(eq(policy), anyInt(), anyInt());
+            verify(zenModeHelper).setNotificationPolicy(any(), eq(policy), anyInt(), anyInt());
         } else {
-            verify(zenModeHelper).applyGlobalPolicyAsImplicitZenRule(anyString(), anyInt(),
+            verify(zenModeHelper).applyGlobalPolicyAsImplicitZenRule(any(), anyString(), anyInt(),
                     eq(policy));
         }
     }
@@ -15892,7 +15927,7 @@
         NotificationManager.Policy policy = new NotificationManager.Policy(0, 0, 0);
         mBinderService.setNotificationPolicy("package", policy, false);
 
-        verify(zenModeHelper).setNotificationPolicy(eq(policy), anyInt(), anyInt());
+        verify(zenModeHelper).setNotificationPolicy(any(), eq(policy), anyInt(), anyInt());
     }
 
     @Test
@@ -15907,7 +15942,7 @@
 
         mBinderService.getNotificationPolicy("package");
 
-        verify(zenHelper).getNotificationPolicyFromImplicitZenRule(eq("package"));
+        verify(zenHelper).getNotificationPolicyFromImplicitZenRule(any(), eq("package"));
     }
 
     @Test
@@ -15922,7 +15957,7 @@
 
         mBinderService.setInterruptionFilter("package", INTERRUPTION_FILTER_PRIORITY, false);
 
-        verify(zenHelper).applyGlobalZenModeAsImplicitZenRule(eq("package"), anyInt(),
+        verify(zenHelper).applyGlobalZenModeAsImplicitZenRule(any(), eq("package"), anyInt(),
                 eq(ZEN_MODE_IMPORTANT_INTERRUPTIONS));
     }
 
@@ -15939,9 +15974,8 @@
 
         mBinderService.setInterruptionFilter("package", INTERRUPTION_FILTER_PRIORITY, false);
 
-        verify(zenModeHelper).setManualZenMode(eq(ZEN_MODE_IMPORTANT_INTERRUPTIONS), eq(null),
-                eq(ZenModeConfig.ORIGIN_SYSTEM), anyString(), eq("package"),
-                anyInt());
+        verify(zenModeHelper).setManualZenMode(any(), eq(ZEN_MODE_IMPORTANT_INTERRUPTIONS),
+                eq(null), eq(ZenModeConfig.ORIGIN_SYSTEM), anyString(), eq("package"), anyInt());
     }
 
     @Test
@@ -15985,10 +16019,10 @@
         mBinderService.setInterruptionFilter("package", INTERRUPTION_FILTER_PRIORITY, false);
 
         if (canSetGlobalPolicy) {
-            verify(zenModeHelper).setManualZenMode(eq(ZEN_MODE_IMPORTANT_INTERRUPTIONS), eq(null),
-                    eq(ZenModeConfig.ORIGIN_APP), anyString(), eq("package"), anyInt());
+            verify(zenModeHelper).setManualZenMode(any(), eq(ZEN_MODE_IMPORTANT_INTERRUPTIONS),
+                    eq(null), eq(ZenModeConfig.ORIGIN_APP), anyString(), eq("package"), anyInt());
         } else {
-            verify(zenModeHelper).applyGlobalZenModeAsImplicitZenRule(anyString(), anyInt(),
+            verify(zenModeHelper).applyGlobalZenModeAsImplicitZenRule(any(), anyString(), anyInt(),
                     eq(ZEN_MODE_IMPORTANT_INTERRUPTIONS));
         }
     }
@@ -16007,8 +16041,8 @@
         mBinderService.requestInterruptionFilterFromListener(mock(INotificationListener.class),
                 INTERRUPTION_FILTER_PRIORITY);
 
-        verify(mService.mZenModeHelper).applyGlobalZenModeAsImplicitZenRule(eq("pkg"), eq(mUid),
-                eq(ZEN_MODE_IMPORTANT_INTERRUPTIONS));
+        verify(mService.mZenModeHelper).applyGlobalZenModeAsImplicitZenRule(any(), eq("pkg"),
+                eq(mUid), eq(ZEN_MODE_IMPORTANT_INTERRUPTIONS));
     }
 
     @Test
@@ -16025,9 +16059,9 @@
         mBinderService.requestInterruptionFilterFromListener(mock(INotificationListener.class),
                 INTERRUPTION_FILTER_PRIORITY);
 
-        verify(mService.mZenModeHelper).setManualZenMode(eq(ZEN_MODE_IMPORTANT_INTERRUPTIONS),
-                eq(null), eq(ZenModeConfig.ORIGIN_SYSTEM), anyString(),
-                eq("pkg"), eq(mUid));
+        verify(mService.mZenModeHelper).setManualZenMode(any(),
+                eq(ZEN_MODE_IMPORTANT_INTERRUPTIONS), eq(null), eq(ZenModeConfig.ORIGIN_SYSTEM),
+                anyString(), eq("pkg"), eq(mUid));
     }
 
     @Test
@@ -16105,8 +16139,8 @@
             throws Exception {
         setUpRealZenTest();
         // Start with hasPriorityChannels=true, allowPriorityChannels=true ("default").
-        mService.mZenModeHelper.setNotificationPolicy(new Policy(0, 0, 0, 0,
-                        Policy.policyState(true, true), 0),
+        mService.mZenModeHelper.setNotificationPolicy(UserHandle.CURRENT,
+                new Policy(0, 0, 0, 0, Policy.policyState(true, true), 0),
                 ZenModeConfig.ORIGIN_SYSTEM, Process.SYSTEM_UID);
 
         // The caller will supply states with "wrong" hasPriorityChannels.
@@ -16136,8 +16170,8 @@
             throws Exception {
         setUpRealZenTest();
         // Start with hasPriorityChannels=true, allowPriorityChannels=true ("default").
-        mService.mZenModeHelper.setNotificationPolicy(new Policy(0, 0, 0, 0,
-                        Policy.policyState(true, true), 0),
+        mService.mZenModeHelper.setNotificationPolicy(UserHandle.CURRENT,
+                new Policy(0, 0, 0, 0, Policy.policyState(true, true), 0),
                 ZenModeConfig.ORIGIN_SYSTEM, Process.SYSTEM_UID);
         mService.setCallerIsNormalPackage();
 
@@ -16767,6 +16801,24 @@
         r.applyAdjustments();
 
         assertThat(r.getChannel().getId()).isEqualTo(NEWS_ID);
+
+        signals.putInt(KEY_TYPE, TYPE_PROMOTION);
+        mBinderService.applyAdjustmentFromAssistant(null, adjustment);
+        waitForIdle();
+        r.applyAdjustments();
+        assertThat(r.getChannel().getId()).isEqualTo(PROMOTIONS_ID);
+
+        signals.putInt(KEY_TYPE, TYPE_SOCIAL_MEDIA);
+        mBinderService.applyAdjustmentFromAssistant(null, adjustment);
+        waitForIdle();
+        r.applyAdjustments();
+        assertThat(r.getChannel().getId()).isEqualTo(SOCIAL_MEDIA_ID);
+
+        signals.putInt(KEY_TYPE, TYPE_CONTENT_RECOMMENDATION);
+        mBinderService.applyAdjustmentFromAssistant(null, adjustment);
+        waitForIdle();
+        r.applyAdjustments();
+        assertThat(r.getChannel().getId()).isEqualTo(RECS_ID);
     }
 
     @Test
@@ -17066,7 +17118,7 @@
     @Test
     @EnableFlags(FLAG_NOTIFICATION_CLASSIFICATION)
     public void testAppCannotUseReservedBundleChannels() throws Exception {
-        mBinderService.getBubblePreferenceForPackage(mPkg, mUid);
+        mService.mPreferencesHelper.createReservedChannel(mPkg, mUid, TYPE_NEWS);
         NotificationChannel news = mBinderService.getNotificationChannel(
                 mPkg, mContext.getUserId(), mPkg, NEWS_ID);
         assertThat(news).isNotNull();
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
index 36fa1b8..e1b478c 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -54,6 +54,11 @@
 import static android.os.UserHandle.USER_ALL;
 import static android.os.UserHandle.USER_SYSTEM;
 import static android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT;
+import static android.service.notification.Adjustment.TYPE_CONTENT_RECOMMENDATION;
+import static android.service.notification.Adjustment.TYPE_NEWS;
+import static android.service.notification.Adjustment.TYPE_OTHER;
+import static android.service.notification.Adjustment.TYPE_PROMOTION;
+import static android.service.notification.Adjustment.TYPE_SOCIAL_MEDIA;
 import static android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION;
 import static android.service.notification.Flags.notificationClassification;
 
@@ -354,7 +359,7 @@
 
         mTestNotificationPolicy = new NotificationManager.Policy(0, 0, 0, 0,
                 NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND, 0);
-        when(mMockZenModeHelper.getNotificationPolicy()).thenReturn(mTestNotificationPolicy);
+        when(mMockZenModeHelper.getNotificationPolicy(any())).thenReturn(mTestNotificationPolicy);
         when(mAppOpsManager.noteOpNoThrow(anyInt(), anyInt(),
                 anyString(), eq(null), anyString())).thenReturn(MODE_DEFAULT);
 
@@ -488,7 +493,7 @@
 
     private void resetZenModeHelper() {
         reset(mMockZenModeHelper);
-        when(mMockZenModeHelper.getNotificationPolicy()).thenReturn(mTestNotificationPolicy);
+        when(mMockZenModeHelper.getNotificationPolicy(any())).thenReturn(mTestNotificationPolicy);
     }
 
     private void setUpPackageWithUid(String packageName, int uid) throws Exception {
@@ -640,6 +645,7 @@
 
         NotificationChannel updateNews = null;
         if (notificationClassification()) {
+            mHelper.createReservedChannel(PKG_N_MR1, UID_N_MR1, TYPE_NEWS);
             // change one of the reserved bundle channels to ensure changes are persisted across
             // boot
             updateNews = mHelper.getNotificationChannel(
@@ -1210,22 +1216,9 @@
                 + "app_user_locked_fields=\"0\" sent_invalid_msg=\"false\" "
                 + "sent_valid_msg=\"false\" user_demote_msg_app=\"false\" sent_valid_bubble"
                 + "=\"false\" uid=\"10002\">\n"
-                + (notificationClassification() ? "<channel id=\"android.app.social\" "
-                + "name=\"Social\" importance=\"2\" "
-                + "sound=\"content://settings/system/notification_sound\" "
-                + "usage=\"5\" content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n" : "")
                 + "<channel id=\"id\" name=\"name\" importance=\"2\" "
                 + "sound=\"content://settings/system/notification_sound\" usage=\"5\" "
                 + "content_type=\"4\" flags=\"0\" show_badge=\"true\" orig_imp=\"2\" />\n"
-                + (notificationClassification() ? "<channel id=\"android.app.news\" name=\"News\" "
-                + "importance=\"2\" sound=\"content://settings/system/notification_sound\" "
-                + "usage=\"5\" content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n"
-                + "<channel id=\"android.app.recs\" name=\"Recommendations\" importance=\"2\" "
-                + "sound=\"content://settings/system/notification_sound\" usage=\"5\" "
-                + "content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n"
-                + "<channel id=\"android.app.promotions\" name=\"Promotions\" importance=\"2\" "
-                + "sound=\"content://settings/system/notification_sound\" usage=\"5\" "
-                + "content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n" : "")
                 + "</package>\n"
                 + "<package name=\"com.example.n_mr1\" show_badge=\"true\" "
                 + "app_user_locked_fields=\"0\" sent_invalid_msg=\"false\" "
@@ -1233,10 +1226,6 @@
                 + "=\"false\" uid=\"10001\">\n"
                 + "<channelGroup id=\"1\" name=\"bye\" blocked=\"false\" locked=\"0\" />\n"
                 + "<channelGroup id=\"2\" name=\"hello\" blocked=\"false\" locked=\"0\" />\n"
-                + (notificationClassification() ? "<channel id=\"android.app.social\" "
-                + "name=\"Social\" importance=\"2\" "
-                + "sound=\"content://settings/system/notification_sound\" "
-                + "usage=\"5\" content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n" : "")
                 + "<channel id=\"id1\" name=\"name1\" importance=\"4\" show_badge=\"true\" "
                 + "orig_imp=\"4\" />\n"
                 + "<channel id=\"id2\" name=\"name2\" desc=\"descriptions for all\" "
@@ -1246,15 +1235,6 @@
                 + "sound=\"content://settings/system/notification_sound\" usage=\"5\" "
                 + "content_type=\"4\" flags=\"0\" vibration_enabled=\"true\" show_badge=\"true\" "
                 + "orig_imp=\"4\" />\n"
-                + (notificationClassification() ? "<channel id=\"android.app.news\" name=\"News\" "
-                + "importance=\"2\" sound=\"content://settings/system/notification_sound\" "
-                + "usage=\"5\" content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n"
-                + "<channel id=\"android.app.recs\" name=\"Recommendations\" importance=\"2\" "
-                + "sound=\"content://settings/system/notification_sound\" usage=\"5\" "
-                + "content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n"
-                + "<channel id=\"android.app.promotions\" name=\"Promotions\" importance=\"2\" "
-                + "sound=\"content://settings/system/notification_sound\" usage=\"5\" "
-                + "content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n" : "")
                 + "<channel id=\"miscellaneous\" name=\"Uncategorized\" "
                 + "sound=\"content://settings/system/notification_sound\" usage=\"5\" "
                 + "content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n"
@@ -1321,22 +1301,9 @@
                 + "app_user_locked_fields=\"0\" sent_invalid_msg=\"false\" "
                 + "sent_valid_msg=\"false\" user_demote_msg_app=\"false\" sent_valid_bubble"
                 + "=\"false\">\n"
-                + (notificationClassification() ? "<channel id=\"android.app.social\" "
-                + "name=\"Social\" importance=\"2\" "
-                + "sound=\"content://settings/system/notification_sound\" "
-                + "usage=\"5\" content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n" : "")
                 + "<channel id=\"id\" name=\"name\" importance=\"2\" "
                 + "sound=\"content://settings/system/notification_sound\" usage=\"5\" "
                 + "content_type=\"4\" flags=\"0\" show_badge=\"true\" orig_imp=\"2\" />\n"
-                + (notificationClassification() ? "<channel id=\"android.app.news\" name=\"News\" "
-                + "importance=\"2\" sound=\"content://settings/system/notification_sound\" "
-                + "usage=\"5\" content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n"
-                + "<channel id=\"android.app.recs\" name=\"Recommendations\" importance=\"2\" "
-                + "sound=\"content://settings/system/notification_sound\" usage=\"5\" "
-                + "content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n"
-                + "<channel id=\"android.app.promotions\" name=\"Promotions\" importance=\"2\" "
-                + "sound=\"content://settings/system/notification_sound\" usage=\"5\" "
-                + "content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n" : "")
                 + "</package>\n"
                 // Importance default because on in permission helper
                 + "<package name=\"com.example.n_mr1\" importance=\"3\" show_badge=\"true\" "
@@ -1345,10 +1312,6 @@
                 + "=\"false\">\n"
                 + "<channelGroup id=\"1\" name=\"bye\" blocked=\"false\" locked=\"0\" />\n"
                 + "<channelGroup id=\"2\" name=\"hello\" blocked=\"false\" locked=\"0\" />\n"
-                + (notificationClassification() ? "<channel id=\"android.app.social\" "
-                + "name=\"Social\" importance=\"2\" "
-                + "sound=\"content://settings/system/notification_sound\" "
-                + "usage=\"5\" content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n" : "")
                 + "<channel id=\"id1\" name=\"name1\" importance=\"4\" show_badge=\"true\" "
                 + "orig_imp=\"4\" />\n"
                 + "<channel id=\"id2\" name=\"name2\" desc=\"descriptions for all\" "
@@ -1358,15 +1321,6 @@
                 + "sound=\"content://settings/system/notification_sound\" usage=\"5\" "
                 + "content_type=\"4\" flags=\"0\" vibration_enabled=\"true\" show_badge=\"true\" "
                 + "orig_imp=\"4\" />\n"
-                + (notificationClassification() ? "<channel id=\"android.app.news\" name=\"News\" "
-                + "importance=\"2\" sound=\"content://settings/system/notification_sound\" "
-                + "usage=\"5\" content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n"
-                + "<channel id=\"android.app.recs\" name=\"Recommendations\" importance=\"2\" "
-                + "sound=\"content://settings/system/notification_sound\" usage=\"5\" "
-                + "content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n"
-                + "<channel id=\"android.app.promotions\" name=\"Promotions\" importance=\"2\" "
-                + "sound=\"content://settings/system/notification_sound\" usage=\"5\" "
-                + "content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n" : "")
                 + "<channel id=\"miscellaneous\" name=\"Uncategorized\" "
                 + "sound=\"content://settings/system/notification_sound\" usage=\"5\" "
                 + "content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n"
@@ -1433,22 +1387,9 @@
                 + "app_user_locked_fields=\"0\" sent_invalid_msg=\"false\" "
                 + "sent_valid_msg=\"false\" user_demote_msg_app=\"false\" sent_valid_bubble"
                 + "=\"false\">\n"
-                + (notificationClassification() ? "<channel id=\"android.app.social\" "
-                + "name=\"Social\" importance=\"2\" "
-                + "sound=\"content://settings/system/notification_sound\" usage=\"5\" "
-                + "content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n" : "")
                 + "<channel id=\"id\" name=\"name\" importance=\"2\" "
                 + "sound=\"content://settings/system/notification_sound\" usage=\"5\" "
                 + "content_type=\"4\" flags=\"0\" show_badge=\"true\" orig_imp=\"2\" />\n"
-                + (notificationClassification() ? "<channel id=\"android.app.news\" name=\"News\" "
-                + "importance=\"2\" sound=\"content://settings/system/notification_sound\" "
-                + "usage=\"5\" content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n"
-                + "<channel id=\"android.app.recs\" name=\"Recommendations\" importance=\"2\" "
-                + "sound=\"content://settings/system/notification_sound\" usage=\"5\" "
-                + "content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n"
-                + "<channel id=\"android.app.promotions\" name=\"Promotions\" importance=\"2\" "
-                + "sound=\"content://settings/system/notification_sound\" usage=\"5\" "
-                + "content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n" : "")
                 + "</package>\n"
                 // Importance 0 because missing from permission helper
                 + "<package name=\"com.example.n_mr1\" importance=\"0\" show_badge=\"true\" "
@@ -1457,10 +1398,6 @@
                 + "=\"false\">\n"
                 + "<channelGroup id=\"1\" name=\"bye\" blocked=\"false\" locked=\"0\" />\n"
                 + "<channelGroup id=\"2\" name=\"hello\" blocked=\"false\" locked=\"0\" />\n"
-                + (notificationClassification() ? "<channel id=\"android.app.social\" "
-                + "name=\"Social\" importance=\"2\" "
-                + "sound=\"content://settings/system/notification_sound\" usage=\"5\" "
-                + "content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n" : "")
                 + "<channel id=\"id1\" name=\"name1\" importance=\"4\" show_badge=\"true\" "
                 + "orig_imp=\"4\" />\n"
                 + "<channel id=\"id2\" name=\"name2\" desc=\"descriptions for all\" "
@@ -1470,15 +1407,6 @@
                 + "sound=\"content://settings/system/notification_sound\" usage=\"5\" "
                 + "content_type=\"4\" flags=\"0\" vibration_enabled=\"true\" show_badge=\"true\" "
                 + "orig_imp=\"4\" />\n"
-                + (notificationClassification() ? "<channel id=\"android.app.news\" name=\"News\" "
-                + "importance=\"2\" sound=\"content://settings/system/notification_sound\" "
-                + "usage=\"5\" content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n"
-                + "<channel id=\"android.app.recs\" name=\"Recommendations\" importance=\"2\" "
-                + "sound=\"content://settings/system/notification_sound\" usage=\"5\" "
-                + "content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n"
-                + "<channel id=\"android.app.promotions\" name=\"Promotions\" importance=\"2\" "
-                + "sound=\"content://settings/system/notification_sound\" usage=\"5\" "
-                + "content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n" : "")
                 + "<channel id=\"miscellaneous\" name=\"Uncategorized\" "
                 + "sound=\"content://settings/system/notification_sound\" usage=\"5\" "
                 + "content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n"
@@ -2183,10 +2111,10 @@
     }
 
     @Test
-    @DisableFlags(FLAG_NOTIFICATION_CLASSIFICATION)
     public void testUpdate_preUpgrade_updatesAppFields() throws Exception {
         assertTrue(mHelper.canShowBadge(PKG_N_MR1, UID_N_MR1));
-        assertEquals(Notification.PRIORITY_DEFAULT, mHelper.getPackagePriority(PKG_N_MR1, UID_N_MR1));
+        assertEquals(Notification.PRIORITY_DEFAULT,
+                mHelper.getPackagePriority(PKG_N_MR1, UID_N_MR1));
         assertEquals(VISIBILITY_NO_OVERRIDE,
                 mHelper.getPackageVisibility(PKG_N_MR1, UID_N_MR1));
 
@@ -2549,7 +2477,7 @@
         List<NotificationChannel> channels =
                 mHelper.getNotificationChannels(PKG_N_MR1, UID_N_MR1, false, true).getList();
         // Default channel + non-deleted channel + system defaults
-        assertEquals(notificationClassification() ? 6 : 2, channels.size());
+        assertEquals(2, channels.size());
         for (NotificationChannel nc : channels) {
             if (channel2.getId().equals(nc.getId())) {
                 compareChannels(channel2, nc);
@@ -2559,7 +2487,7 @@
         // Returns deleted channels too
         channels = mHelper.getNotificationChannels(PKG_N_MR1, UID_N_MR1, true, true).getList();
         // Includes system channel(s)
-        assertEquals(notificationClassification() ? 7 : 3, channels.size());
+        assertEquals(3, channels.size());
         for (NotificationChannel nc : channels) {
             if (channel2.getId().equals(nc.getId())) {
                 compareChannels(channelMap.get(nc.getId()), nc);
@@ -2704,9 +2632,10 @@
                 uid, false);
         assertFalse(mHelper.areChannelsBypassingDnd());
         if (android.app.Flags.modesUi()) {
-            verify(mMockZenModeHelper, never()).updateHasPriorityChannels(anyBoolean());
+            verify(mMockZenModeHelper, never()).updateHasPriorityChannels(any(), anyBoolean());
         } else {
-            verify(mMockZenModeHelper, never()).setNotificationPolicy(any(), anyInt(), anyInt());
+            verify(mMockZenModeHelper, never()).setNotificationPolicy(any(), any(), anyInt(),
+                    anyInt());
         }
         resetZenModeHelper();
 
@@ -2718,9 +2647,11 @@
                 uid, false);
         assertTrue(mHelper.areChannelsBypassingDnd());
         if (android.app.Flags.modesUi()) {
-            verify(mMockZenModeHelper, times(1)).updateHasPriorityChannels(eq(true));
+            verify(mMockZenModeHelper, times(1)).updateHasPriorityChannels(eq(UserHandle.CURRENT),
+                    eq(true));
         } else {
-            verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any(), anyInt(), anyInt());
+            verify(mMockZenModeHelper, times(1)).setNotificationPolicy(eq(UserHandle.CURRENT),
+                    any(), anyInt(), anyInt());
         }
         resetZenModeHelper();
 
@@ -2728,18 +2659,21 @@
         mHelper.deleteNotificationChannel(PKG_N_MR1, uid, channel.getId(), uid, false);
         assertTrue(mHelper.areChannelsBypassingDnd()); // channel2 can still bypass DND
         if (android.app.Flags.modesUi()) {
-            verify(mMockZenModeHelper, never()).updateHasPriorityChannels(anyBoolean());
+            verify(mMockZenModeHelper, never()).updateHasPriorityChannels(any(), anyBoolean());
         } else {
-            verify(mMockZenModeHelper, never()).setNotificationPolicy(any(), anyInt(), anyInt());
+            verify(mMockZenModeHelper, never()).setNotificationPolicy(any(), any(), anyInt(),
+                    anyInt());
         }
         resetZenModeHelper();
 
         mHelper.deleteNotificationChannel(PKG_N_MR1, uid, channel2.getId(), uid, false);
         assertFalse(mHelper.areChannelsBypassingDnd());
         if (android.app.Flags.modesUi()) {
-            verify(mMockZenModeHelper, times(1)).updateHasPriorityChannels(eq(false));
+            verify(mMockZenModeHelper, times(1)).updateHasPriorityChannels(eq(UserHandle.CURRENT),
+                    eq(false));
         } else {
-            verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any(), anyInt(), anyInt());
+            verify(mMockZenModeHelper, times(1)).setNotificationPolicy(eq(UserHandle.CURRENT),
+                    any(), anyInt(), anyInt());
         }
         resetZenModeHelper();
     }
@@ -2757,9 +2691,10 @@
                 uid, false);
         assertFalse(mHelper.areChannelsBypassingDnd());
         if (android.app.Flags.modesUi()) {
-            verify(mMockZenModeHelper, never()).updateHasPriorityChannels(anyBoolean());
+            verify(mMockZenModeHelper, never()).updateHasPriorityChannels(any(), anyBoolean());
         } else {
-            verify(mMockZenModeHelper, never()).setNotificationPolicy(any(), anyInt(), anyInt());
+            verify(mMockZenModeHelper, never()).setNotificationPolicy(any(), any(), anyInt(),
+                    anyInt());
         }
         resetZenModeHelper();
 
@@ -2771,9 +2706,11 @@
 
         assertTrue(mHelper.areChannelsBypassingDnd());
         if (android.app.Flags.modesUi()) {
-            verify(mMockZenModeHelper, times(1)).updateHasPriorityChannels(eq(true));
+            verify(mMockZenModeHelper, times(1)).updateHasPriorityChannels(eq(UserHandle.CURRENT),
+                    eq(true));
         } else {
-            verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any(), anyInt(), anyInt());
+            verify(mMockZenModeHelper, times(1)).setNotificationPolicy(eq(UserHandle.CURRENT),
+                    any(), anyInt(), anyInt());
         }
         resetZenModeHelper();
     }
@@ -2791,9 +2728,10 @@
                 uid, false);
         assertFalse(mHelper.areChannelsBypassingDnd());
         if (android.app.Flags.modesUi()) {
-            verify(mMockZenModeHelper, never()).updateHasPriorityChannels(anyBoolean());
+            verify(mMockZenModeHelper, never()).updateHasPriorityChannels(any(), anyBoolean());
         } else {
-            verify(mMockZenModeHelper, never()).setNotificationPolicy(any(), anyInt(), anyInt());
+            verify(mMockZenModeHelper, never()).setNotificationPolicy(any(), any(), anyInt(),
+                    anyInt());
         }
         resetZenModeHelper();
 
@@ -2805,9 +2743,11 @@
                 uid, false);
         assertTrue(mHelper.areChannelsBypassingDnd());
         if (android.app.Flags.modesUi()) {
-            verify(mMockZenModeHelper, times(1)).updateHasPriorityChannels(eq(true));
+            verify(mMockZenModeHelper, times(1)).updateHasPriorityChannels(eq(UserHandle.CURRENT),
+                    eq(true));
         } else {
-            verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any(), anyInt(), anyInt());
+            verify(mMockZenModeHelper, times(1)).setNotificationPolicy(eq(UserHandle.CURRENT),
+                    any(), anyInt(), anyInt());
         }
         resetZenModeHelper();
 
@@ -2815,18 +2755,21 @@
         mHelper.deleteNotificationChannel(PKG_N_MR1, uid, channel.getId(), uid, false);
         assertTrue(mHelper.areChannelsBypassingDnd()); // channel2 can still bypass DND
         if (android.app.Flags.modesUi()) {
-            verify(mMockZenModeHelper, never()).updateHasPriorityChannels(anyBoolean());
+            verify(mMockZenModeHelper, never()).updateHasPriorityChannels(any(), anyBoolean());
         } else {
-            verify(mMockZenModeHelper, never()).setNotificationPolicy(any(), anyInt(), anyInt());
+            verify(mMockZenModeHelper, never()).setNotificationPolicy(any(), any(), anyInt(),
+                    anyInt());
         }
         resetZenModeHelper();
 
         mHelper.deleteNotificationChannel(PKG_N_MR1, uid, channel2.getId(), uid, false);
         assertFalse(mHelper.areChannelsBypassingDnd());
         if (android.app.Flags.modesUi()) {
-            verify(mMockZenModeHelper, times(1)).updateHasPriorityChannels(eq(false));
+            verify(mMockZenModeHelper, times(1)).updateHasPriorityChannels(eq(UserHandle.CURRENT),
+                    eq(false));
         } else {
-            verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any(), anyInt(), anyInt());
+            verify(mMockZenModeHelper, times(1)).setNotificationPolicy(eq(UserHandle.CURRENT),
+                    any(), anyInt(), anyInt());
         }
         resetZenModeHelper();
     }
@@ -2839,7 +2782,7 @@
         // start in a 'allowed to bypass dnd state'
         mTestNotificationPolicy = new NotificationManager.Policy(0, 0, 0, 0,
                 NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND, 0);
-        when(mMockZenModeHelper.getNotificationPolicy()).thenReturn(mTestNotificationPolicy);
+        when(mMockZenModeHelper.getNotificationPolicy(any())).thenReturn(mTestNotificationPolicy);
         mHelper.syncChannelsBypassingDnd();
 
         // create notification channel that can bypass dnd, but app is blocked
@@ -2855,9 +2798,11 @@
                 uid, false);
         assertFalse(mHelper.areChannelsBypassingDnd());
         if (android.app.Flags.modesUi()) {
-            verify(mMockZenModeHelper, times(1)).updateHasPriorityChannels(eq(false));
+            verify(mMockZenModeHelper, times(1)).updateHasPriorityChannels(eq(UserHandle.CURRENT),
+                    eq(false));
         } else {
-            verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any(), anyInt(), anyInt());
+            verify(mMockZenModeHelper, times(1)).setNotificationPolicy(eq(UserHandle.CURRENT),
+                    any(), anyInt(), anyInt());
         }
         resetZenModeHelper();
     }
@@ -2870,7 +2815,7 @@
         // start in a 'allowed to bypass dnd state'
         mTestNotificationPolicy = new NotificationManager.Policy(0, 0, 0, 0,
                 NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND, 0);
-        when(mMockZenModeHelper.getNotificationPolicy()).thenReturn(mTestNotificationPolicy);
+        when(mMockZenModeHelper.getNotificationPolicy(any())).thenReturn(mTestNotificationPolicy);
         mHelper.syncChannelsBypassingDnd();
 
         // create notification channel that can bypass dnd, but app is blocked
@@ -2881,9 +2826,11 @@
                 uid, false);
         assertFalse(mHelper.areChannelsBypassingDnd());
         if (android.app.Flags.modesUi()) {
-            verify(mMockZenModeHelper, times(1)).updateHasPriorityChannels(eq(false));
+            verify(mMockZenModeHelper, times(1)).updateHasPriorityChannels(eq(UserHandle.CURRENT),
+                    eq(false));
         } else {
-            verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any(), anyInt(), anyInt());
+            verify(mMockZenModeHelper, times(1)).setNotificationPolicy(eq(UserHandle.CURRENT),
+                    any(), anyInt(), anyInt());
         }
         resetZenModeHelper();
     }
@@ -2896,7 +2843,7 @@
         // start in a 'allowed to bypass dnd state'
         mTestNotificationPolicy = new NotificationManager.Policy(0, 0, 0, 0,
                 NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND, 0);
-        when(mMockZenModeHelper.getNotificationPolicy()).thenReturn(mTestNotificationPolicy);
+        when(mMockZenModeHelper.getNotificationPolicy(any())).thenReturn(mTestNotificationPolicy);
         mHelper.syncChannelsBypassingDnd();
 
         // create notification channel that can bypass dnd, but app is blocked
@@ -2907,9 +2854,11 @@
                 uid, false);
         assertFalse(mHelper.areChannelsBypassingDnd());
         if (android.app.Flags.modesUi()) {
-            verify(mMockZenModeHelper, times(1)).updateHasPriorityChannels(eq(false));
+            verify(mMockZenModeHelper, times(1)).updateHasPriorityChannels(eq(UserHandle.CURRENT),
+                    eq(false));
         } else {
-            verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any(), anyInt(), anyInt());
+            verify(mMockZenModeHelper, times(1)).setNotificationPolicy(eq(UserHandle.CURRENT),
+                    any(), anyInt(), anyInt());
         }
         resetZenModeHelper();
     }
@@ -2927,9 +2876,10 @@
                 uid, false);
         assertFalse(mHelper.areChannelsBypassingDnd());
         if (android.app.Flags.modesUi()) {
-            verify(mMockZenModeHelper, never()).updateHasPriorityChannels(anyBoolean());
+            verify(mMockZenModeHelper, never()).updateHasPriorityChannels(any(), anyBoolean());
         } else {
-            verify(mMockZenModeHelper, never()).setNotificationPolicy(any(), anyInt(), anyInt());
+            verify(mMockZenModeHelper, never()).setNotificationPolicy(any(), any(), anyInt(),
+                    anyInt());
         }
         resetZenModeHelper();
 
@@ -2939,9 +2889,11 @@
         mHelper.updateNotificationChannel(PKG_N_MR1, uid, channel, true, SYSTEM_UID, true);
         assertTrue(mHelper.areChannelsBypassingDnd());
         if (android.app.Flags.modesUi()) {
-            verify(mMockZenModeHelper, times(1)).updateHasPriorityChannels(eq(true));
+            verify(mMockZenModeHelper, times(1)).updateHasPriorityChannels(eq(UserHandle.CURRENT),
+                    eq(true));
         } else {
-            verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any(), anyInt(), anyInt());
+            verify(mMockZenModeHelper, times(1)).setNotificationPolicy(eq(UserHandle.CURRENT),
+                    any(), anyInt(), anyInt());
         }
         resetZenModeHelper();
 
@@ -2951,9 +2903,11 @@
         mHelper.updateNotificationChannel(PKG_N_MR1, uid, channel, true, SYSTEM_UID, true);
         assertFalse(mHelper.areChannelsBypassingDnd());
         if (android.app.Flags.modesUi()) {
-            verify(mMockZenModeHelper, times(1)).updateHasPriorityChannels(eq(false));
+            verify(mMockZenModeHelper, times(1)).updateHasPriorityChannels(eq(UserHandle.CURRENT),
+                    eq(false));
         } else {
-            verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any(), anyInt(), anyInt());
+            verify(mMockZenModeHelper, times(1)).setNotificationPolicy(eq(UserHandle.CURRENT),
+                    any(), anyInt(), anyInt());
         }
         resetZenModeHelper();
     }
@@ -2964,13 +2918,15 @@
         // RankingHelper should change to false
         mTestNotificationPolicy = new NotificationManager.Policy(0, 0, 0, 0,
                 NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND, 0);
-        when(mMockZenModeHelper.getNotificationPolicy()).thenReturn(mTestNotificationPolicy);
+        when(mMockZenModeHelper.getNotificationPolicy(any())).thenReturn(mTestNotificationPolicy);
         mHelper.syncChannelsBypassingDnd();
         assertFalse(mHelper.areChannelsBypassingDnd());
         if (android.app.Flags.modesUi()) {
-            verify(mMockZenModeHelper, times(1)).updateHasPriorityChannels(eq(false));
+            verify(mMockZenModeHelper, times(1)).updateHasPriorityChannels(eq(UserHandle.CURRENT),
+                    eq(false));
         } else {
-            verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any(), anyInt(), anyInt());
+            verify(mMockZenModeHelper, times(1)).setNotificationPolicy(eq(UserHandle.CURRENT),
+                    any(), anyInt(), anyInt());
         }
         resetZenModeHelper();
     }
@@ -2979,12 +2935,13 @@
     public void testSetupNewZenModeHelper_cannotBypass() {
         // start notification policy off with mAreChannelsBypassingDnd = false
         mTestNotificationPolicy = new NotificationManager.Policy(0, 0, 0, 0, 0, 0);
-        when(mMockZenModeHelper.getNotificationPolicy()).thenReturn(mTestNotificationPolicy);
+        when(mMockZenModeHelper.getNotificationPolicy(any())).thenReturn(mTestNotificationPolicy);
         assertFalse(mHelper.areChannelsBypassingDnd());
         if (android.app.Flags.modesUi()) {
-            verify(mMockZenModeHelper, never()).updateHasPriorityChannels(anyBoolean());
+            verify(mMockZenModeHelper, never()).updateHasPriorityChannels(any(), anyBoolean());
         } else {
-            verify(mMockZenModeHelper, never()).setNotificationPolicy(any(), anyInt(), anyInt());
+            verify(mMockZenModeHelper, never()).setNotificationPolicy(any(), any(), anyInt(),
+                    anyInt());
         }
         resetZenModeHelper();
     }
@@ -3036,7 +2993,6 @@
     }
 
     @Test
-    @DisableFlags(FLAG_NOTIFICATION_CLASSIFICATION)
     public void testOnlyHasDefaultChannel() throws Exception {
         assertTrue(mHelper.onlyHasDefaultChannel(PKG_N_MR1, UID_N_MR1));
         assertFalse(mHelper.onlyHasDefaultChannel(PKG_O, UID_O));
@@ -3047,6 +3003,18 @@
     }
 
     @Test
+    @EnableFlags(FLAG_NOTIFICATION_CLASSIFICATION)
+    public void testOnlyHasDefaultChannel_bundleExists() throws Exception {
+        mHelper.createReservedChannel(PKG_N_MR1, UID_N_MR1, TYPE_NEWS);
+        assertTrue(mHelper.onlyHasDefaultChannel(PKG_N_MR1, UID_N_MR1));
+        assertFalse(mHelper.onlyHasDefaultChannel(PKG_O, UID_O));
+
+        mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, getChannel(), true, false,
+                UID_N_MR1, false);
+        assertFalse(mHelper.onlyHasDefaultChannel(PKG_N_MR1, UID_N_MR1));
+    }
+
+    @Test
     public void testCreateDeletedChannel() throws Exception {
         long[] vibration = new long[]{100, 67, 145, 156};
         NotificationChannel channel =
@@ -3315,7 +3283,7 @@
 
         // user 0 records remain
         for (int i = 0; i < user0Uids.length; i++) {
-            assertEquals(notificationClassification() ? 5 : 1,
+            assertEquals(1,
                     mHelper.getNotificationChannels(PKG_N_MR1, user0Uids[i], false, true)
                             .getList().size());
         }
@@ -3346,7 +3314,7 @@
 
         assertFalse(mHelper.onPackagesChanged(false, USER_SYSTEM,
                 new String[]{PKG_N_MR1}, new int[]{UID_N_MR1}));
-        assertEquals(notificationClassification() ? 6 : 2,
+        assertEquals(2,
                 mHelper.getNotificationChannels(PKG_N_MR1, UID_N_MR1, false, true)
                         .getList().size());
     }
@@ -3420,7 +3388,7 @@
     @Test
     public void testRecordDefaults() throws Exception {
         assertEquals(true, mHelper.canShowBadge(PKG_N_MR1, UID_N_MR1));
-        assertEquals(notificationClassification() ? 5 : 1,
+        assertEquals(1,
                 mHelper.getNotificationChannels(PKG_N_MR1, UID_N_MR1, false, true)
                         .getList().size());
     }
@@ -3659,9 +3627,6 @@
                         new NotificationChannel("" + j, "a", IMPORTANCE_HIGH), true, false,
                         UID_N_MR1, false);
             }
-            if (notificationClassification()) {
-                numChannels += 4;
-            }
             expectedChannels.put(pkgName, numChannels);
         }
 
@@ -4883,10 +4848,6 @@
     @Test
     public void testTooManyChannels() {
         int numToCreate = NOTIFICATION_CHANNEL_COUNT_LIMIT;
-        if (notificationClassification()) {
-            // reserved channels lower limit
-            numToCreate -= 4;
-        }
         for (int i = 0; i < numToCreate; i++) {
             NotificationChannel channel = new NotificationChannel(String.valueOf(i),
                     String.valueOf(i), NotificationManager.IMPORTANCE_HIGH);
@@ -4907,10 +4868,6 @@
     @Test
     public void testTooManyChannels_xml() throws Exception {
         int numToCreate = NOTIFICATION_CHANNEL_COUNT_LIMIT;
-        if (notificationClassification()) {
-            // reserved channels lower limit
-            numToCreate -= 4;
-        }
         String extraChannel = "EXTRA";
         String extraChannel1 = "EXTRA1";
 
@@ -5928,9 +5885,7 @@
         ArrayList<StatsEvent> events = new ArrayList<>();
         mHelper.pullPackageChannelPreferencesStats(events);
 
-        assertEquals("expected number of events",
-                notificationClassification() ? 7 : 3,
-                events.size());
+        assertEquals("expected number of events", 3, events.size());
         for (StatsEvent ev : events) {
             // all of these events should be of PackageNotificationChannelPreferences type,
             // and therefore we expect the atom to have this field.
@@ -5971,17 +5926,11 @@
         mHelper.createNotificationChannel(PKG_O, UID_O, channelC, true, false, UID_O, false);
 
         List<String> channels = new LinkedList<>(Arrays.asList("a", "b", "c"));
-        if (notificationClassification()) {
-            channels.add(NEWS_ID);
-            channels.add(PROMOTIONS_ID);
-            channels.add(SOCIAL_MEDIA_ID);
-            channels.add(RECS_ID);
-        }
 
         ArrayList<StatsEvent> events = new ArrayList<>();
         mHelper.pullPackageChannelPreferencesStats(events);
 
-        assertEquals("total events", notificationClassification() ? 7 : 3, events.size());
+        assertEquals("total events", 3, events.size());
         for (StatsEvent ev : events) {
             AtomsProto.Atom atom = StatsEventTestUtils.convertToAtom(ev);
             assertTrue(atom.hasPackageNotificationChannelPreferences());
@@ -6015,7 +5964,7 @@
         mHelper.pullPackageChannelPreferencesStats(events);
 
         // In this case, we want to check the properties of the conversation channel (not parent)
-        assertEquals("total events", notificationClassification() ? 6 : 2, events.size());
+        assertEquals("total events", 2, events.size());
         for (StatsEvent ev : events) {
             AtomsProto.Atom atom = StatsEventTestUtils.convertToAtom(ev);
             assertTrue(atom.hasPackageNotificationChannelPreferences());
@@ -6047,9 +5996,7 @@
         ArrayList<StatsEvent> events = new ArrayList<>();
         mHelper.pullPackageChannelPreferencesStats(events);
 
-        assertEquals("total events",
-                notificationClassification() ? 6 : 2,
-                events.size());
+        assertEquals("total events", 2, events.size());
         for (StatsEvent ev : events) {
             AtomsProto.Atom atom = StatsEventTestUtils.convertToAtom(ev);
             assertTrue(atom.hasPackageNotificationChannelPreferences());
@@ -6080,9 +6027,7 @@
         ArrayList<StatsEvent> events = new ArrayList<>();
         mHelper.pullPackageChannelPreferencesStats(events);
 
-        assertEquals("total events",
-                notificationClassification() ? 6 : 2,
-                events.size());
+        assertEquals("total events", 2, events.size());
         for (StatsEvent ev : events) {
             AtomsProto.Atom atom = StatsEventTestUtils.convertToAtom(ev);
             assertTrue(atom.hasPackageNotificationChannelPreferences());
@@ -6367,8 +6312,7 @@
     @Test
     @EnableFlags(FLAG_NOTIFICATION_CLASSIFICATION)
     public void testGetNotificationChannels_omitBundleChannels() {
-        // do something that triggers settings creation for an app
-        mHelper.setShowBadge(PKG_O, UID_O, true);
+        mHelper.createReservedChannel(PKG_O, UID_O, TYPE_NEWS);
 
         assertThat(mHelper.getNotificationChannels(PKG_O, UID_O, true, false).getList()).isEmpty();
     }
@@ -6376,18 +6320,34 @@
     @Test
     @EnableFlags(FLAG_NOTIFICATION_CLASSIFICATION)
     public void testNotificationBundles() {
-        // do something that triggers settings creation for an app
-        mHelper.setShowBadge(PKG_O, UID_O, true);
-
-        // verify 4 reserved channels are created
+        mHelper.createReservedChannel(PKG_O, UID_O, TYPE_NEWS);
         assertThat(mHelper.getNotificationChannel(PKG_O, UID_O, NEWS_ID, false).getImportance())
                 .isEqualTo(IMPORTANCE_LOW);
-        assertThat(mHelper.getNotificationChannel(PKG_O, UID_O, PROMOTIONS_ID, false)
-                .getImportance()).isEqualTo(IMPORTANCE_LOW);
-        assertThat(mHelper.getNotificationChannel(PKG_O, UID_O, SOCIAL_MEDIA_ID, false)
-                .getImportance()).isEqualTo(IMPORTANCE_LOW);
+        assertThat(mHelper.getNotificationChannels(PKG_O, UID_O, true, true).getList().size())
+                .isEqualTo(1);
+
+        mHelper.createReservedChannel(PKG_O, UID_O, TYPE_SOCIAL_MEDIA);
+        assertThat(mHelper.getNotificationChannel(PKG_O, UID_O, SOCIAL_MEDIA_ID, false).
+                getImportance()).isEqualTo(IMPORTANCE_LOW);
+        assertThat(mHelper.getNotificationChannels(PKG_O, UID_O, true, true).getList().size())
+                .isEqualTo(2);
+
+        mHelper.createReservedChannel(PKG_O, UID_O, TYPE_CONTENT_RECOMMENDATION);
         assertThat(mHelper.getNotificationChannel(PKG_O, UID_O, RECS_ID, false).getImportance())
                 .isEqualTo(IMPORTANCE_LOW);
+        assertThat(mHelper.getNotificationChannels(PKG_O, UID_O, true, true).getList().size())
+                .isEqualTo(3);
+
+        mHelper.createReservedChannel(PKG_O, UID_O, TYPE_PROMOTION);
+        assertThat(mHelper.getNotificationChannel(PKG_O, UID_O, PROMOTIONS_ID, false)
+                .getImportance()).isEqualTo(IMPORTANCE_LOW);
+        assertThat(mHelper.getNotificationChannels(PKG_O, UID_O, true, true).getList().size())
+                .isEqualTo(4);
+
+        // only the first 4 types are created; no others
+        mHelper.createReservedChannel(PKG_O, UID_O, TYPE_OTHER);
+        assertThat(mHelper.getNotificationChannels(PKG_O, UID_O, true, true).getList().size())
+                .isEqualTo(4);
     }
 
     @Test
@@ -6417,8 +6377,7 @@
     @Test
     @EnableFlags(FLAG_NOTIFICATION_CLASSIFICATION)
     public void testNotificationBundles_appsCannotUpdate() {
-        // do something that triggers settings creation for an app
-        mHelper.setShowBadge(PKG_O, UID_O, true);
+        mHelper.createReservedChannel(PKG_O, UID_O, TYPE_NEWS);
 
         NotificationChannel fromApp =
                 new NotificationChannel(NEWS_ID, "The best channel", IMPORTANCE_HIGH);
@@ -6431,8 +6390,7 @@
     @Test
     @EnableFlags(FLAG_NOTIFICATION_CLASSIFICATION)
     public void testNotificationBundles_osCanAllowToBypassDnd() {
-        // do something that triggers settings creation for an app
-        mHelper.setShowBadge(PKG_O, UID_O, true);
+        mHelper.createReservedChannel(PKG_O, UID_O, TYPE_NEWS);
 
         NotificationChannel fromApp =
                 new NotificationChannel(NEWS_ID, "The best channel", IMPORTANCE_HIGH);
@@ -6442,18 +6400,17 @@
     @Test
     @EnableFlags(FLAG_NOTIFICATION_CLASSIFICATION)
     public void testUnDeleteBundleChannelsOnLoadIfNotUserChange() throws Exception {
-        mHelper.setShowBadge(PKG_P, UID_P, true);
         // the public create/update methods should prevent this, so take advantage of the fact that
         // the object is in the same process
-        mHelper.getNotificationChannel(PKG_P, UID_P, SOCIAL_MEDIA_ID, true).setDeleted(true);
+        mHelper.createReservedChannel(PKG_N_MR1, UID_N_MR1, TYPE_SOCIAL_MEDIA).setDeleted(true);
 
         ByteArrayOutputStream baos = writeXmlAndPurge(PKG_N_MR1, UID_N_MR1, false,
                 UserHandle.USER_ALL, SOCIAL_MEDIA_ID);
 
         loadStreamXml(baos, false, UserHandle.USER_ALL);
 
-        assertThat(mXmlHelper.getNotificationChannel(PKG_P, UID_P, SOCIAL_MEDIA_ID, true).
-                isDeleted()).isFalse();
+        assertThat(mXmlHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, SOCIAL_MEDIA_ID, true)
+                .isDeleted()).isFalse();
     }
 
     @Test
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java
index 5d4382a..f900346 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java
@@ -155,7 +155,7 @@
 
         mTestNotificationPolicy = new NotificationManager.Policy(0, 0, 0, 0,
                 NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND, 0);
-        when(mMockZenModeHelper.getNotificationPolicy()).thenReturn(mTestNotificationPolicy);
+        when(mMockZenModeHelper.getNotificationPolicy(any())).thenReturn(mTestNotificationPolicy);
         mHelper = new RankingHelper(getContext(), mHandler, mConfig, mMockZenModeHelper,
                 mUsageStats, new String[] {ImportanceExtractor.class.getName()},
                 mock(IPlatformCompat.class), mGroupHelper);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/SystemZenRulesTest.java b/services/tests/uiservicestests/src/com/android/server/notification/SystemZenRulesTest.java
index 5de323b..4d82c3c 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/SystemZenRulesTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/SystemZenRulesTest.java
@@ -209,18 +209,30 @@
     }
 
     @Test
-    public void getShortDaysSummary_onlyDays() {
+    public void getDaysOfWeekShort_summarizesDays() {
         ScheduleInfo scheduleInfo = new ScheduleInfo();
         scheduleInfo.startHour = 10;
         scheduleInfo.endHour = 16;
         scheduleInfo.days = new int[] {Calendar.MONDAY, Calendar.TUESDAY,
                 Calendar.WEDNESDAY, Calendar.THURSDAY, Calendar.FRIDAY};
 
-        assertThat(SystemZenRules.getShortDaysSummary(mContext, scheduleInfo))
+        assertThat(SystemZenRules.getDaysOfWeekShort(mContext, scheduleInfo))
                 .isEqualTo("Mon-Fri");
     }
 
     @Test
+    public void getDaysOfWeekFull_summarizesDays() {
+        ScheduleInfo scheduleInfo = new ScheduleInfo();
+        scheduleInfo.startHour = 10;
+        scheduleInfo.endHour = 16;
+        scheduleInfo.days = new int[] {Calendar.MONDAY, Calendar.TUESDAY,
+                Calendar.WEDNESDAY, Calendar.THURSDAY, Calendar.FRIDAY};
+
+        assertThat(SystemZenRules.getDaysOfWeekFull(mContext, scheduleInfo))
+                .isEqualTo("Monday to Friday");
+    }
+
+    @Test
     public void getTimeSummary_onlyTime() {
         ScheduleInfo scheduleInfo = new ScheduleInfo();
         scheduleInfo.startHour = 11;
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/TimeToLiveHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/TimeToLiveHelperTest.java
index ad6c233..09a6840 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/TimeToLiveHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/TimeToLiveHelperTest.java
@@ -19,6 +19,7 @@
 import static com.google.common.truth.Truth.assertThat;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
@@ -108,6 +109,21 @@
     }
 
     @Test
+    public void testTimeoutExpiresButNotifEntryGone() {
+        NotificationRecord r = getRecord("testTimeoutExpires", 1);
+
+        mHelper.scheduleTimeoutLocked(r, 1);
+        ArgumentCaptor<PendingIntent> captor = ArgumentCaptor.forClass(PendingIntent.class);
+        verify(mAm).setExactAndAllowWhileIdle(anyInt(), eq(2L), captor.capture());
+
+        mHelper.mKeys.clear();
+
+        mHelper.mNotificationTimeoutReceiver.onReceive(mContext, captor.getValue().getIntent());
+
+        verify(mNm, never()).timeoutNotification(anyString());
+    }
+
+    @Test
     public void testTimeoutExpires_twoEntries() {
         NotificationRecord first = getRecord("testTimeoutFirst", 1);
         NotificationRecord later = getRecord("testTimeoutSecond", 2);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
index 8b3ac2b..a196594 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
@@ -21,6 +21,7 @@
 import static android.app.AutomaticZenRule.TYPE_SCHEDULE_TIME;
 import static android.app.AutomaticZenRule.TYPE_UNKNOWN;
 import static android.app.Flags.FLAG_MODES_API;
+import static android.app.Flags.FLAG_MODES_MULTIUSER;
 import static android.app.Flags.FLAG_MODES_UI;
 import static android.app.NotificationManager.AUTOMATIC_RULE_STATUS_ACTIVATED;
 import static android.app.NotificationManager.AUTOMATIC_RULE_STATUS_DEACTIVATED;
@@ -61,6 +62,7 @@
 import static android.service.notification.ZenModeConfig.ORIGIN_APP;
 import static android.service.notification.ZenModeConfig.ORIGIN_INIT;
 import static android.service.notification.ZenModeConfig.ORIGIN_INIT_USER;
+import static android.service.notification.ZenModeConfig.ORIGIN_SYSTEM;
 import static android.service.notification.ZenModeConfig.ORIGIN_UNKNOWN;
 import static android.service.notification.ZenModeConfig.ORIGIN_USER_IN_APP;
 import static android.service.notification.ZenModeConfig.ORIGIN_USER_IN_SYSTEMUI;
@@ -689,13 +691,12 @@
         AutomaticZenRule azr = new AutomaticZenRule.Builder("OriginalName", CONDITION_ID)
                 .setInterruptionFilter(INTERRUPTION_FILTER_NONE)
                 .build();
-        String ruleId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(),
-                azr, ZenModeConfig.ORIGIN_SYSTEM, "reason", SYSTEM_UID);
+        String ruleId = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT,
+                mContext.getPackageName(), azr, ORIGIN_SYSTEM, "reason", SYSTEM_UID);
 
         // Enable rule
-        mZenModeHelper.setAutomaticZenRuleState(ruleId,
-                new Condition(azr.getConditionId(), "", STATE_TRUE),
-                ZenModeConfig.ORIGIN_SYSTEM,
+        mZenModeHelper.setAutomaticZenRuleState(UserHandle.CURRENT, ruleId,
+                new Condition(azr.getConditionId(), "", STATE_TRUE), ORIGIN_SYSTEM,
                 SYSTEM_UID);
 
         // Confirm that the consolidated policy doesn't allow anything
@@ -723,13 +724,12 @@
         AutomaticZenRule azr = new AutomaticZenRule.Builder("OriginalName", CONDITION_ID)
                 .setInterruptionFilter(INTERRUPTION_FILTER_ALARMS)
                 .build();
-        String ruleId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(),
-                azr, ZenModeConfig.ORIGIN_SYSTEM, "reason", SYSTEM_UID);
+        String ruleId = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT,
+                mContext.getPackageName(), azr, ORIGIN_SYSTEM, "reason", SYSTEM_UID);
 
         // Enable rule
-        mZenModeHelper.setAutomaticZenRuleState(ruleId,
-                new Condition(azr.getConditionId(), "", STATE_TRUE),
-                ZenModeConfig.ORIGIN_SYSTEM,
+        mZenModeHelper.setAutomaticZenRuleState(UserHandle.CURRENT, ruleId,
+                new Condition(azr.getConditionId(), "", STATE_TRUE), ORIGIN_SYSTEM,
                 SYSTEM_UID);
 
         // Confirm that the consolidated policy allows only alarms and media and nothing else
@@ -756,7 +756,7 @@
         when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_NORMAL);
         // Set zen to priority-only with all notification sounds muted (so ringer will be muted)
         Policy totalSilence = new Policy(0, 0, 0);
-        mZenModeHelper.setNotificationPolicy(totalSilence, ORIGIN_APP, 1);
+        mZenModeHelper.setNotificationPolicy(UserHandle.CURRENT, totalSilence, ORIGIN_APP, 1);
         mZenModeHelper.mZenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS;
 
         // 2. verify ringer is unchanged
@@ -793,8 +793,8 @@
     public void testRingerAffectedStreamsPriorityOnly() {
         // in priority only mode:
         // ringtone, notification and system streams are affected by ringer mode
-        mZenModeHelper.setManualZenMode(ZEN_MODE_IMPORTANT_INTERRUPTIONS, Uri.EMPTY,
-                ORIGIN_APP, "test", "caller", 1);
+        mZenModeHelper.setManualZenMode(UserHandle.CURRENT, ZEN_MODE_IMPORTANT_INTERRUPTIONS,
+                Uri.EMPTY, ORIGIN_APP, "test", "caller", 1);
         ZenModeHelper.RingerModeDelegate ringerModeDelegateRingerMuted =
                 mZenModeHelper.new RingerModeDelegate();
 
@@ -810,9 +810,10 @@
 
         // even when ringer is muted (since all ringer sounds cannot bypass DND),
         // system stream is still affected by ringer mode
-        mZenModeHelper.setNotificationPolicy(new Policy(0, 0, 0), ORIGIN_APP, 1);
-        mZenModeHelper.setManualZenMode(ZEN_MODE_IMPORTANT_INTERRUPTIONS, Uri.EMPTY,
-                ORIGIN_APP, "test", "caller", 1);
+        mZenModeHelper.setNotificationPolicy(UserHandle.CURRENT, new Policy(0, 0, 0), ORIGIN_APP,
+                1);
+        mZenModeHelper.setManualZenMode(UserHandle.CURRENT, ZEN_MODE_IMPORTANT_INTERRUPTIONS,
+                Uri.EMPTY, ORIGIN_APP, "test", "caller", 1);
         ZenModeHelper.RingerModeDelegate ringerModeDelegateRingerNotMuted =
                 mZenModeHelper.new RingerModeDelegate();
 
@@ -919,7 +920,7 @@
         // apply zen off multiple times - verify ringer is not set to normal
         when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_SILENT);
         for (int i = 0; i < 3; i++) {
-            mZenModeHelper.setManualZenMode(ZEN_MODE_OFF, Uri.EMPTY,
+            mZenModeHelper.setManualZenMode(UserHandle.CURRENT, ZEN_MODE_OFF, Uri.EMPTY,
                     ORIGIN_APP, "test", "caller", 1);
         }
         verify(mAudioManager, never()).setRingerModeInternal(AudioManager.RINGER_MODE_NORMAL,
@@ -944,7 +945,7 @@
         when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_SILENT);
         for (int i = 0; i < 3; i++) {
             // if zen doesn't change, zen should not reapply itself to the ringer
-            mZenModeHelper.setManualZenMode(ZEN_MODE_OFF, Uri.EMPTY,
+            mZenModeHelper.setManualZenMode(UserHandle.CURRENT, ZEN_MODE_OFF, Uri.EMPTY,
                     ORIGIN_APP, "test", "caller", 1);
         }
         verify(mAudioManager, never()).setRingerModeInternal(AudioManager.RINGER_MODE_NORMAL,
@@ -969,7 +970,7 @@
         when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_VIBRATE);
         for (int i = 0; i < 3; i++) {
             // if zen doesn't change, zen should not reapply itself to the ringer
-            mZenModeHelper.setManualZenMode(ZEN_MODE_OFF, Uri.EMPTY,
+            mZenModeHelper.setManualZenMode(UserHandle.CURRENT, ZEN_MODE_OFF, Uri.EMPTY,
                     ORIGIN_APP, "test", "caller", 1);
         }
         verify(mAudioManager, never()).setRingerModeInternal(AudioManager.RINGER_MODE_NORMAL,
@@ -985,9 +986,8 @@
         reset(mAudioManager);
 
         // Turn manual zen mode on
-        mZenModeHelper.setManualZenMode(ZEN_MODE_IMPORTANT_INTERRUPTIONS, null,
-                ORIGIN_APP,
-                null, "test", CUSTOM_PKG_UID);
+        mZenModeHelper.setManualZenMode(UserHandle.CURRENT, ZEN_MODE_IMPORTANT_INTERRUPTIONS, null,
+                ORIGIN_APP, null, "test", CUSTOM_PKG_UID);
 
         // audio manager shouldn't do anything until the handler processes its messages
         verify(mAudioManager, never()).updateRingerModeAffectedStreamsInternal();
@@ -1012,6 +1012,7 @@
 
         // Turn manual zen mode on
         mZenModeHelper.setManualZenMode(
+                UserHandle.CURRENT,
                 ZEN_MODE_IMPORTANT_INTERRUPTIONS,
                 null,
                 ORIGIN_APP,
@@ -1019,6 +1020,7 @@
                 "test",
                 CUSTOM_PKG_UID);
         mZenModeHelper.setManualZenMode(
+                UserHandle.CURRENT,
                 ZEN_MODE_IMPORTANT_INTERRUPTIONS,
                 null,
                 ORIGIN_APP,
@@ -1042,19 +1044,22 @@
 
     @Test
     public void testParcelConfig() {
-        mZenModeHelper.setNotificationPolicy(new Policy(PRIORITY_CATEGORY_EVENTS
+        mZenModeHelper.setNotificationPolicy(UserHandle.CURRENT,
+                new Policy(PRIORITY_CATEGORY_EVENTS
                         | PRIORITY_CATEGORY_MESSAGES | PRIORITY_CATEGORY_REPEAT_CALLERS
                         | PRIORITY_CATEGORY_CONVERSATIONS, PRIORITY_SENDERS_STARRED,
                         PRIORITY_SENDERS_STARRED, 0, CONVERSATION_SENDERS_ANYONE),
                 ORIGIN_UNKNOWN,
                 1);
-        mZenModeHelper.setManualZenRuleDeviceEffects(new ZenDeviceEffects.Builder()
-                .setShouldDimWallpaper(true)
-                .setShouldDisplayGrayscale(true)
-                .setShouldUseNightMode(true)
-                .build(), ORIGIN_UNKNOWN, "test", 1);
-        mZenModeHelper.setManualZenMode(ZEN_MODE_IMPORTANT_INTERRUPTIONS, Uri.EMPTY,
-                ORIGIN_UNKNOWN, "test", "me", 1);
+        mZenModeHelper.setManualZenRuleDeviceEffects(UserHandle.CURRENT,
+                new ZenDeviceEffects.Builder()
+                        .setShouldDimWallpaper(true)
+                        .setShouldDisplayGrayscale(true)
+                        .setShouldUseNightMode(true)
+                        .build(),
+                ORIGIN_UNKNOWN, "test", 1);
+        mZenModeHelper.setManualZenMode(UserHandle.CURRENT, ZEN_MODE_IMPORTANT_INTERRUPTIONS,
+                Uri.EMPTY, ORIGIN_UNKNOWN, "test", "me", 1);
 
         ZenModeConfig actual = mZenModeHelper.mConfig.copy();
 
@@ -1063,18 +1068,21 @@
 
     @Test
     public void testWriteXml() throws Exception {
-        mZenModeHelper.setNotificationPolicy(new Policy(PRIORITY_CATEGORY_EVENTS
+        mZenModeHelper.setNotificationPolicy(UserHandle.CURRENT,
+                new Policy(PRIORITY_CATEGORY_EVENTS
                         | PRIORITY_CATEGORY_MESSAGES | PRIORITY_CATEGORY_REPEAT_CALLERS
                         | PRIORITY_CATEGORY_CONVERSATIONS, PRIORITY_SENDERS_STARRED,
                         PRIORITY_SENDERS_STARRED, SUPPRESSED_EFFECT_BADGE,
                         CONVERSATION_SENDERS_ANYONE),
                 ORIGIN_UNKNOWN, 1);
-        mZenModeHelper.setManualZenRuleDeviceEffects(new ZenDeviceEffects.Builder()
-                .setShouldDimWallpaper(true)
-                .setShouldDisplayGrayscale(true)
-                .build(), ORIGIN_UNKNOWN, "test", 1);
-        mZenModeHelper.setManualZenMode(ZEN_MODE_IMPORTANT_INTERRUPTIONS, Uri.EMPTY,
-                ORIGIN_UNKNOWN, "test", "me", 1);
+        mZenModeHelper.setManualZenRuleDeviceEffects(UserHandle.CURRENT,
+                new ZenDeviceEffects.Builder()
+                        .setShouldDimWallpaper(true)
+                        .setShouldDisplayGrayscale(true)
+                        .build(),
+                ORIGIN_UNKNOWN, "test", 1);
+        mZenModeHelper.setManualZenMode(UserHandle.CURRENT, ZEN_MODE_IMPORTANT_INTERRUPTIONS,
+                Uri.EMPTY, ORIGIN_UNKNOWN, "test", "me", 1);
 
         ZenModeConfig expected = mZenModeHelper.mConfig.copy();
         if (Flags.modesUi()) {
@@ -1094,9 +1102,9 @@
 
     @Test
     public void testProto() throws InvalidProtocolBufferException {
-        mZenModeHelper.setManualZenMode(ZEN_MODE_IMPORTANT_INTERRUPTIONS, null,
-                Flags.modesApi() ? ORIGIN_USER_IN_SYSTEMUI : ZenModeConfig.ORIGIN_SYSTEM,
-                null, "test", CUSTOM_PKG_UID);
+        mZenModeHelper.setManualZenMode(UserHandle.CURRENT, ZEN_MODE_IMPORTANT_INTERRUPTIONS, null,
+                Flags.modesApi() ? ORIGIN_USER_IN_SYSTEMUI : ORIGIN_SYSTEM, null,
+                "test", CUSTOM_PKG_UID);
 
         mZenModeHelper.mConfig.automaticRules = new ArrayMap<>(); // no automatic rules
 
@@ -1319,8 +1327,8 @@
         List<StatsEvent> events = new LinkedList<>();
 
         mZenModeHelper.pullRules(events);
-        mZenModeHelper.removeAutomaticZenRule(CUSTOM_RULE_ID, ORIGIN_APP, "test",
-                CUSTOM_PKG_UID);
+        mZenModeHelper.removeAutomaticZenRule(UserHandle.CURRENT, CUSTOM_RULE_ID, ORIGIN_APP,
+                "test", CUSTOM_PKG_UID);
         assertTrue(-1
                 == mZenModeHelper.mRulesUidCache.getOrDefault(CUSTOM_PKG_NAME + "|" + 0, -1));
     }
@@ -1348,9 +1356,8 @@
     public void testProtoWithManualRule() throws Exception {
         setupZenConfig();
         mZenModeHelper.mConfig.automaticRules = getCustomAutomaticRules();
-        mZenModeHelper.setManualZenMode(INTERRUPTION_FILTER_PRIORITY, Uri.EMPTY,
-                ORIGIN_APP,
-                "test", "me", 1);
+        mZenModeHelper.setManualZenMode(UserHandle.CURRENT, INTERRUPTION_FILTER_PRIORITY, Uri.EMPTY,
+                ORIGIN_APP, "test", "me", 1);
 
         List<StatsEvent> events = new LinkedList<>();
         mZenModeHelper.pullRules(events);
@@ -1457,7 +1464,7 @@
         setupZenConfig();
 
         // Turn on manual zen mode
-        mZenModeHelper.setManualZenMode(ZEN_MODE_IMPORTANT_INTERRUPTIONS, null,
+        mZenModeHelper.setManualZenMode(UserHandle.CURRENT, ZEN_MODE_IMPORTANT_INTERRUPTIONS, null,
                 ORIGIN_USER_IN_SYSTEMUI, "", "someCaller", SYSTEM_UID);
         ZenModeConfig original = mZenModeHelper.mConfig.copy();
         assertThat(original.isManualActive()).isTrue();
@@ -1574,7 +1581,7 @@
     @Test
     public void testReadXmlRulesNotOverridden() throws Exception {
         setupZenConfig();
-        Policy originalPolicy = mZenModeHelper.getNotificationPolicy();
+        Policy originalPolicy = mZenModeHelper.getNotificationPolicy(UserHandle.CURRENT);
 
         // automatic zen rule is enabled on upgrade so rules should not be overriden to default
         ArrayMap<String, ZenModeConfig.ZenRule> enabledAutoRule = new ArrayMap<>();
@@ -1597,7 +1604,7 @@
         mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL);
 
         assertTrue(mZenModeHelper.mConfig.automaticRules.containsKey("customRule"));
-        assertEquals(originalPolicy, mZenModeHelper.getNotificationPolicy());
+        assertEquals(originalPolicy, mZenModeHelper.getNotificationPolicy(UserHandle.CURRENT));
     }
 
     @Test
@@ -1727,7 +1734,7 @@
     @Test
     public void testReadXmlResetDefaultRules() throws Exception {
         setupZenConfig();
-        Policy originalPolicy = mZenModeHelper.getNotificationPolicy();
+        Policy originalPolicy = mZenModeHelper.getNotificationPolicy(UserHandle.CURRENT);
 
         // no enabled automatic zen rules and no default rules
         // so rules should be overridden by default rules
@@ -1748,13 +1755,13 @@
             assertTrue(rules.containsKey(defaultId));
         }
 
-        assertEquals(originalPolicy, mZenModeHelper.getNotificationPolicy());
+        assertEquals(originalPolicy, mZenModeHelper.getNotificationPolicy(UserHandle.CURRENT));
     }
 
     @Test
     public void testReadXmlAllDisabledRulesResetDefaultRules() throws Exception {
         setupZenConfig();
-        Policy originalPolicy = mZenModeHelper.getNotificationPolicy();
+        Policy originalPolicy = mZenModeHelper.getNotificationPolicy(UserHandle.CURRENT);
 
         // all automatic zen rules are disabled on upgrade (and default rules don't already exist)
         // so rules should be overriden by default rules
@@ -1785,14 +1792,14 @@
         }
         assertFalse(rules.containsKey("customRule"));
 
-        assertEquals(originalPolicy, mZenModeHelper.getNotificationPolicy());
+        assertEquals(originalPolicy, mZenModeHelper.getNotificationPolicy(UserHandle.CURRENT));
     }
 
     @Test
     @DisableFlags(FLAG_MODES_UI) // modes_ui has only 1 default rule
     public void testReadXmlOnlyOneDefaultRuleExists() throws Exception {
         setupZenConfig();
-        Policy originalPolicy = mZenModeHelper.getNotificationPolicy();
+        Policy originalPolicy = mZenModeHelper.getNotificationPolicy(UserHandle.CURRENT);
 
         // all automatic zen rules are disabled on upgrade and only one default rule exists
         // so rules should be overriden to the default rules
@@ -1839,13 +1846,13 @@
         }
         assertThat(rules).doesNotContainKey("customRule");
 
-        assertEquals(originalPolicy, mZenModeHelper.getNotificationPolicy());
+        assertEquals(originalPolicy, mZenModeHelper.getNotificationPolicy(UserHandle.CURRENT));
     }
 
     @Test
     public void testReadXmlDefaultRulesExist() throws Exception {
         setupZenConfig();
-        Policy originalPolicy = mZenModeHelper.getNotificationPolicy();
+        Policy originalPolicy = mZenModeHelper.getNotificationPolicy(UserHandle.CURRENT);
 
         // Default rules exist so rules should not be overridden by defaults
         ArrayMap<String, ZenModeConfig.ZenRule> automaticRules = new ArrayMap<>();
@@ -1910,7 +1917,7 @@
         }
         assertThat(rules).containsKey("customRule");
 
-        assertEquals(originalPolicy, mZenModeHelper.getNotificationPolicy());
+        assertEquals(originalPolicy, mZenModeHelper.getNotificationPolicy(UserHandle.CURRENT));
 
         List<StatsEvent> events = new LinkedList<>();
         mZenModeHelper.pullRules(events);
@@ -1923,7 +1930,7 @@
         // When reading XML for something that is already on the modes API system, make sure no
         // rules' policies get changed.
         setupZenConfig();
-        Policy originalPolicy = mZenModeHelper.getNotificationPolicy();
+        Policy originalPolicy = mZenModeHelper.getNotificationPolicy(UserHandle.CURRENT);
 
         // Shared for rules
         ArrayMap<String, ZenModeConfig.ZenRule> enabledAutoRules = new ArrayMap<>();
@@ -1955,7 +1962,7 @@
         mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL);
 
         // basic check: global config maintained
-        assertEquals(originalPolicy, mZenModeHelper.getNotificationPolicy());
+        assertEquals(originalPolicy, mZenModeHelper.getNotificationPolicy(UserHandle.CURRENT));
 
         // Find our automatic rules.
         ArrayMap<String, ZenModeConfig.ZenRule> rules = mZenModeHelper.mConfig.automaticRules;
@@ -1972,7 +1979,7 @@
         // a custom policy matching the global config for any automatic rule with no specified
         // policy.
         setupZenConfig();
-        Policy originalPolicy = mZenModeHelper.getNotificationPolicy();
+        Policy originalPolicy = mZenModeHelper.getNotificationPolicy(UserHandle.CURRENT);
 
         ArrayMap<String, ZenModeConfig.ZenRule> enabledAutoRule = new ArrayMap<>();
         ZenModeConfig.ZenRule customRule = new ZenModeConfig.ZenRule();
@@ -1994,7 +2001,7 @@
         mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL);
 
         // basic check: global config maintained
-        assertEquals(originalPolicy, mZenModeHelper.getNotificationPolicy());
+        assertEquals(originalPolicy, mZenModeHelper.getNotificationPolicy(UserHandle.CURRENT));
 
         // Find our automatic rule and check that it has a policy set now
         ArrayMap<String, ZenModeConfig.ZenRule> rules = mZenModeHelper.mConfig.automaticRules;
@@ -2024,7 +2031,7 @@
         // underspecified ZenPolicy, we fill in all of the gaps with things from the global config
         // in order to maintain consistency of behavior.
         setupZenConfig();
-        Policy originalPolicy = mZenModeHelper.getNotificationPolicy();
+        Policy originalPolicy = mZenModeHelper.getNotificationPolicy(UserHandle.CURRENT);
 
         ArrayMap<String, ZenModeConfig.ZenRule> enabledAutoRule = new ArrayMap<>();
         ZenModeConfig.ZenRule customRule = new ZenModeConfig.ZenRule();
@@ -2051,7 +2058,7 @@
         mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL);
 
         // basic check: global config maintained
-        assertEquals(originalPolicy, mZenModeHelper.getNotificationPolicy());
+        assertEquals(originalPolicy, mZenModeHelper.getNotificationPolicy(UserHandle.CURRENT));
 
         // Find our automatic rule and check that it has a policy set now
         ArrayMap<String, ZenModeConfig.ZenRule> rules = mZenModeHelper.mConfig.automaticRules;
@@ -2345,8 +2352,8 @@
                     NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
             // We need the package name to be something that's not "android" so there aren't any
             // existing rules under that package.
-            String id = mZenModeHelper.addAutomaticZenRule("pkgname", zenRule, ORIGIN_APP,
-                    "test", CUSTOM_PKG_UID);
+            String id = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT, "pkgname", zenRule,
+                    ORIGIN_APP, "test", CUSTOM_PKG_UID);
             assertNotNull(id);
         }
         try {
@@ -2356,8 +2363,8 @@
                     ZenModeConfig.toScheduleConditionId(new ScheduleInfo()),
                     new ZenPolicy.Builder().build(),
                     NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
-            String id = mZenModeHelper.addAutomaticZenRule("pkgname", zenRule, ORIGIN_APP,
-                    "test", CUSTOM_PKG_UID);
+            String id = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT, "pkgname", zenRule,
+                    ORIGIN_APP, "test", CUSTOM_PKG_UID);
             fail("allowed too many rules to be created");
         } catch (IllegalArgumentException e) {
             // yay
@@ -2377,8 +2384,8 @@
                     ZenModeConfig.toScheduleConditionId(si),
                     new ZenPolicy.Builder().build(),
                     NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
-            String id = mZenModeHelper.addAutomaticZenRule("pkgname", zenRule, ORIGIN_APP,
-                    "test", CUSTOM_PKG_UID);
+            String id = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT, "pkgname", zenRule,
+                    ORIGIN_APP, "test", CUSTOM_PKG_UID);
             assertNotNull(id);
         }
         try {
@@ -2388,8 +2395,8 @@
                     ZenModeConfig.toScheduleConditionId(new ScheduleInfo()),
                     new ZenPolicy.Builder().build(),
                     NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
-            String id = mZenModeHelper.addAutomaticZenRule("pkgname", zenRule, ORIGIN_APP,
-                    "test", CUSTOM_PKG_UID);
+            String id = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT, "pkgname", zenRule,
+                    ORIGIN_APP, "test", CUSTOM_PKG_UID);
             fail("allowed too many rules to be created");
         } catch (IllegalArgumentException e) {
             // yay
@@ -2409,8 +2416,8 @@
                     ZenModeConfig.toScheduleConditionId(si),
                     new ZenPolicy.Builder().build(),
                     NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
-            String id = mZenModeHelper.addAutomaticZenRule("pkgname", zenRule, ORIGIN_APP,
-                    "test", CUSTOM_PKG_UID);
+            String id = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT, "pkgname", zenRule,
+                    ORIGIN_APP, "test", CUSTOM_PKG_UID);
             assertNotNull(id);
         }
         try {
@@ -2420,8 +2427,8 @@
                     ZenModeConfig.toScheduleConditionId(new ScheduleInfo()),
                     new ZenPolicy.Builder().build(),
                     NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
-            String id = mZenModeHelper.addAutomaticZenRule("pkgname", zenRule, ORIGIN_APP,
-                    "test", CUSTOM_PKG_UID);
+            String id = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT, "pkgname", zenRule,
+                    ORIGIN_APP, "test", CUSTOM_PKG_UID);
             fail("allowed too many rules to be created");
         } catch (IllegalArgumentException e) {
             // yay
@@ -2436,8 +2443,8 @@
                 ZenModeConfig.toScheduleConditionId(new ScheduleInfo()),
                 new ZenPolicy.Builder().build(),
                 NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
-        String id = mZenModeHelper.addAutomaticZenRule("android", zenRule,
-                ZenModeConfig.ORIGIN_SYSTEM, "test", SYSTEM_UID);
+        String id = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT, "android", zenRule,
+                ORIGIN_SYSTEM, "test", SYSTEM_UID);
 
         assertTrue(id != null);
         ZenModeConfig.ZenRule ruleInConfig = mZenModeHelper.mConfig.automaticRules.get(id);
@@ -2457,8 +2464,8 @@
                 new ComponentName("android", "ScheduleConditionProvider"),
                 ZenModeConfig.toScheduleConditionId(new ScheduleInfo()),
                 NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
-        String id = mZenModeHelper.addAutomaticZenRule("android", zenRule,
-                ZenModeConfig.ORIGIN_SYSTEM, "test", SYSTEM_UID);
+        String id = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT, "android", zenRule,
+                ORIGIN_SYSTEM, "test", SYSTEM_UID);
 
         assertTrue(id != null);
         ZenModeConfig.ZenRule ruleInConfig = mZenModeHelper.mConfig.automaticRules.get(id);
@@ -2483,8 +2490,8 @@
                 new ComponentName("android", "ScheduleConditionProvider"),
                 ZenModeConfig.toScheduleConditionId(new ScheduleInfo()),
                 NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
-        String id1 = mZenModeHelper.addAutomaticZenRule("android", zenRule1,
-                ZenModeConfig.ORIGIN_SYSTEM, "test", SYSTEM_UID);
+        String id1 = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT, "android", zenRule1,
+                ORIGIN_SYSTEM, "test", SYSTEM_UID);
 
         // Zen rule with partially-filled policy: should get all of the filled fields set, and the
         // rest filled with default state
@@ -2498,8 +2505,8 @@
                         .showFullScreenIntent(true)
                         .build(),
                 NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
-        String id2 = mZenModeHelper.addAutomaticZenRule("android", zenRule2,
-                ZenModeConfig.ORIGIN_SYSTEM, "test", SYSTEM_UID);
+        String id2 = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT, "android", zenRule2,
+                ORIGIN_SYSTEM, "test", SYSTEM_UID);
 
         // rule 1 should exist
         assertThat(id1).isNotNull();
@@ -2544,9 +2551,9 @@
                 new ZenPolicy.Builder().build(),
                 NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
 
-        String id = mZenModeHelper.addAutomaticZenRule(null, zenRule, ORIGIN_APP, "test",
-                CUSTOM_PKG_UID);
-        mZenModeHelper.setAutomaticZenRuleState(zenRule.getConditionId(),
+        String id = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT, null, zenRule,
+                ORIGIN_APP, "test", CUSTOM_PKG_UID);
+        mZenModeHelper.setAutomaticZenRuleState(UserHandle.CURRENT, zenRule.getConditionId(),
                 new Condition(zenRule.getConditionId(), "", STATE_TRUE),
                 ORIGIN_APP,
                 CUSTOM_PKG_UID);
@@ -2564,8 +2571,8 @@
                 new ZenPolicy.Builder().build(),
                 NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
 
-        String id = mZenModeHelper.addAutomaticZenRule(null, zenRule, ORIGIN_APP, "test",
-                CUSTOM_PKG_UID);
+        String id = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT, null, zenRule,
+                ORIGIN_APP, "test", CUSTOM_PKG_UID);
 
         AutomaticZenRule zenRule2 = new AutomaticZenRule("NEW",
                 null,
@@ -2574,7 +2581,8 @@
                 new ZenPolicy.Builder().build(),
                 NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
 
-        mZenModeHelper.updateAutomaticZenRule(id, zenRule2, ORIGIN_APP, "", CUSTOM_PKG_UID);
+        mZenModeHelper.updateAutomaticZenRule(UserHandle.CURRENT, id, zenRule2, ORIGIN_APP, "",
+                CUSTOM_PKG_UID);
 
         ZenModeConfig.ZenRule ruleInConfig = mZenModeHelper.mConfig.automaticRules.get(id);
         assertEquals("NEW", ruleInConfig.name);
@@ -2589,15 +2597,16 @@
                 new ZenPolicy.Builder().build(),
                 NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
 
-        String id = mZenModeHelper.addAutomaticZenRule(null, zenRule, ORIGIN_APP, "test",
-                CUSTOM_PKG_UID);
+        String id = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT, null, zenRule,
+                ORIGIN_APP, "test", CUSTOM_PKG_UID);
 
         assertTrue(id != null);
         ZenModeConfig.ZenRule ruleInConfig = mZenModeHelper.mConfig.automaticRules.get(id);
         assertTrue(ruleInConfig != null);
         assertEquals(zenRule.getName(), ruleInConfig.name);
 
-        mZenModeHelper.removeAutomaticZenRule(id, ORIGIN_APP, "test", CUSTOM_PKG_UID);
+        mZenModeHelper.removeAutomaticZenRule(UserHandle.CURRENT, id, ORIGIN_APP, "test",
+                CUSTOM_PKG_UID);
         assertNull(mZenModeHelper.mConfig.automaticRules.get(id));
     }
 
@@ -2609,16 +2618,16 @@
                 ZenModeConfig.toScheduleConditionId(new ScheduleInfo()),
                 new ZenPolicy.Builder().build(),
                 NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
-        String id = mZenModeHelper.addAutomaticZenRule(null, zenRule, ORIGIN_APP, "test",
-                CUSTOM_PKG_UID);
+        String id = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT, null, zenRule,
+                ORIGIN_APP, "test", CUSTOM_PKG_UID);
 
         assertTrue(id != null);
         ZenModeConfig.ZenRule ruleInConfig = mZenModeHelper.mConfig.automaticRules.get(id);
         assertTrue(ruleInConfig != null);
         assertEquals(zenRule.getName(), ruleInConfig.name);
 
-        mZenModeHelper.removeAutomaticZenRules(mContext.getPackageName(), ORIGIN_APP, "test",
-                CUSTOM_PKG_UID);
+        mZenModeHelper.removeAutomaticZenRules(UserHandle.CURRENT, mContext.getPackageName(),
+                ORIGIN_APP, "test", CUSTOM_PKG_UID);
         assertNull(mZenModeHelper.mConfig.automaticRules.get(id));
     }
 
@@ -2633,18 +2642,18 @@
                 new ComponentName(mPkg, "ScheduleConditionProvider"),
                 sharedUri,
                 NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
-        String id = mZenModeHelper.addAutomaticZenRule(mPkg, zenRule,
-                ZenModeConfig.ORIGIN_SYSTEM, "test", SYSTEM_UID);
+        String id = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT, mPkg, zenRule,
+                ORIGIN_SYSTEM, "test", SYSTEM_UID);
         AutomaticZenRule zenRule2 = new AutomaticZenRule("name2",
                 new ComponentName(mPkg, "ScheduleConditionProvider"),
                 sharedUri,
                 NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
-        String id2 = mZenModeHelper.addAutomaticZenRule(mPkg, zenRule2,
-                ZenModeConfig.ORIGIN_SYSTEM, "test", SYSTEM_UID);
+        String id2 = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT, mPkg, zenRule2,
+                ORIGIN_SYSTEM, "test", SYSTEM_UID);
 
         Condition condition = new Condition(sharedUri, "", STATE_TRUE);
-        mZenModeHelper.setAutomaticZenRuleState(sharedUri, condition,
-                ZenModeConfig.ORIGIN_SYSTEM, SYSTEM_UID);
+        mZenModeHelper.setAutomaticZenRuleState(UserHandle.CURRENT, sharedUri, condition,
+                ORIGIN_SYSTEM, SYSTEM_UID);
 
         for (ZenModeConfig.ZenRule rule : mZenModeHelper.mConfig.automaticRules.values()) {
             if (rule.id.equals(id)) {
@@ -2658,8 +2667,8 @@
         }
 
         condition = new Condition(sharedUri, "", STATE_FALSE);
-        mZenModeHelper.setAutomaticZenRuleState(sharedUri, condition,
-                ZenModeConfig.ORIGIN_SYSTEM, SYSTEM_UID);
+        mZenModeHelper.setAutomaticZenRuleState(UserHandle.CURRENT, sharedUri, condition,
+                ORIGIN_SYSTEM, SYSTEM_UID);
 
         for (ZenModeConfig.ZenRule rule : mZenModeHelper.mConfig.automaticRules.values()) {
             if (rule.id.equals(id)) {
@@ -2689,14 +2698,15 @@
                 .setShouldMaximizeDoze(true)
                 .build();
 
-        String ruleId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(),
+        String ruleId = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT,
+                mContext.getPackageName(),
                 new AutomaticZenRule.Builder("Rule", CONDITION_ID)
                         .setOwner(OWNER)
                         .setDeviceEffects(zde)
                         .build(),
                 ORIGIN_APP, "reasons", 0);
 
-        AutomaticZenRule savedRule = mZenModeHelper.getAutomaticZenRule(ruleId);
+        AutomaticZenRule savedRule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId);
         assertThat(savedRule.getDeviceEffects()).isEqualTo(
                 new ZenDeviceEffects.Builder()
                         .setShouldDisplayGrayscale(true)
@@ -2722,14 +2732,15 @@
                 .setShouldMaximizeDoze(true)
                 .build();
 
-        String ruleId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(),
+        String ruleId = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT,
+                mContext.getPackageName(),
                 new AutomaticZenRule.Builder("Rule", CONDITION_ID)
                         .setOwner(OWNER)
                         .setDeviceEffects(zde)
                         .build(),
-                ZenModeConfig.ORIGIN_SYSTEM, "reasons", 0);
+                ORIGIN_SYSTEM, "reasons", 0);
 
-        AutomaticZenRule savedRule = mZenModeHelper.getAutomaticZenRule(ruleId);
+        AutomaticZenRule savedRule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId);
         assertThat(savedRule.getDeviceEffects()).isEqualTo(zde);
     }
 
@@ -2749,7 +2760,8 @@
                 .setShouldMaximizeDoze(true)
                 .build();
 
-        String ruleId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(),
+        String ruleId = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT,
+                mContext.getPackageName(),
                 new AutomaticZenRule.Builder("Rule", CONDITION_ID)
                         .setOwner(OWNER)
                         .setDeviceEffects(zde)
@@ -2757,7 +2769,7 @@
                 ORIGIN_USER_IN_SYSTEMUI,
                 "reasons", 0);
 
-        AutomaticZenRule savedRule = mZenModeHelper.getAutomaticZenRule(ruleId);
+        AutomaticZenRule savedRule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId);
 
         assertThat(savedRule.getDeviceEffects()).isEqualTo(zde);
     }
@@ -2769,26 +2781,27 @@
                 .setShouldDisableTapToWake(true)
                 .addExtraEffect("extra")
                 .build();
-        String ruleId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(),
+        String ruleId = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT,
+                mContext.getPackageName(),
                 new AutomaticZenRule.Builder("Rule", CONDITION_ID)
                         .setOwner(OWNER)
                         .setDeviceEffects(original)
                         .build(),
-                ZenModeConfig.ORIGIN_SYSTEM, "reasons", 0);
+                ORIGIN_SYSTEM, "reasons", 0);
 
         ZenDeviceEffects updateFromApp = new ZenDeviceEffects.Builder()
                 .setShouldUseNightMode(true) // Good
                 .setShouldMaximizeDoze(true) // Bad
                 .addExtraEffect("should be rejected") // Bad
                 .build();
-        mZenModeHelper.updateAutomaticZenRule(ruleId,
+        mZenModeHelper.updateAutomaticZenRule(UserHandle.CURRENT, ruleId,
                 new AutomaticZenRule.Builder("Rule", CONDITION_ID)
                         .setOwner(OWNER)
                         .setDeviceEffects(updateFromApp)
                         .build(),
                 ORIGIN_APP, "reasons", 0);
 
-        AutomaticZenRule savedRule = mZenModeHelper.getAutomaticZenRule(ruleId);
+        AutomaticZenRule savedRule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId);
         assertThat(savedRule.getDeviceEffects()).isEqualTo(
                 new ZenDeviceEffects.Builder()
                         .setShouldUseNightMode(true) // From update.
@@ -2803,24 +2816,25 @@
         ZenDeviceEffects original = new ZenDeviceEffects.Builder()
                 .setShouldDisableTapToWake(true)
                 .build();
-        String ruleId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(),
+        String ruleId = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT,
+                mContext.getPackageName(),
                 new AutomaticZenRule.Builder("Rule", CONDITION_ID)
                         .setOwner(OWNER)
                         .setDeviceEffects(original)
                         .build(),
-                ZenModeConfig.ORIGIN_SYSTEM, "reasons", 0);
+                ORIGIN_SYSTEM, "reasons", 0);
 
         ZenDeviceEffects updateFromSystem = new ZenDeviceEffects.Builder()
                 .setShouldUseNightMode(true) // Good
                 .setShouldMaximizeDoze(true) // Also good
                 .build();
-        mZenModeHelper.updateAutomaticZenRule(ruleId,
+        mZenModeHelper.updateAutomaticZenRule(UserHandle.CURRENT, ruleId,
                 new AutomaticZenRule.Builder("Rule", CONDITION_ID)
                         .setDeviceEffects(updateFromSystem)
                         .build(),
-                ZenModeConfig.ORIGIN_SYSTEM, "reasons", 0);
+                ORIGIN_SYSTEM, "reasons", 0);
 
-        AutomaticZenRule savedRule = mZenModeHelper.getAutomaticZenRule(ruleId);
+        AutomaticZenRule savedRule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId);
         assertThat(savedRule.getDeviceEffects()).isEqualTo(updateFromSystem);
     }
 
@@ -2830,12 +2844,13 @@
         ZenDeviceEffects original = new ZenDeviceEffects.Builder()
                 .setShouldDisableTapToWake(true)
                 .build();
-        String ruleId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(),
+        String ruleId = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT,
+                mContext.getPackageName(),
                 new AutomaticZenRule.Builder("Rule", CONDITION_ID)
                         .setOwner(OWNER)
                         .setDeviceEffects(original)
                         .build(),
-                ZenModeConfig.ORIGIN_SYSTEM, "reasons", 0);
+                ORIGIN_SYSTEM, "reasons", 0);
 
         ZenDeviceEffects updateFromUser = new ZenDeviceEffects.Builder()
                 .setShouldUseNightMode(true)
@@ -2844,13 +2859,13 @@
                 // even with this line removed, tap to wake would be set to false.
                 .setShouldDisableTapToWake(false)
                 .build();
-        mZenModeHelper.updateAutomaticZenRule(ruleId,
+        mZenModeHelper.updateAutomaticZenRule(UserHandle.CURRENT, ruleId,
                 new AutomaticZenRule.Builder("Rule", CONDITION_ID)
                         .setDeviceEffects(updateFromUser)
                         .build(),
                 ORIGIN_USER_IN_SYSTEMUI, "reasons", 0);
 
-        AutomaticZenRule savedRule = mZenModeHelper.getAutomaticZenRule(ruleId);
+        AutomaticZenRule savedRule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId);
 
         assertThat(savedRule.getDeviceEffects()).isEqualTo(updateFromUser);
     }
@@ -2860,7 +2875,8 @@
     public void updateAutomaticZenRule_nullPolicy_doesNothing() {
         // Test that when updateAutomaticZenRule is called with a null policy, nothing changes
         // about the existing policy.
-        String ruleId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(),
+        String ruleId = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT,
+                mContext.getPackageName(),
                 new AutomaticZenRule.Builder("Rule", CONDITION_ID)
                         .setOwner(OWNER)
                         .setZenPolicy(new ZenPolicy.Builder()
@@ -2869,13 +2885,13 @@
                         .build(),
                 ORIGIN_APP, "reasons", 0);
 
-        mZenModeHelper.updateAutomaticZenRule(ruleId,
+        mZenModeHelper.updateAutomaticZenRule(UserHandle.CURRENT, ruleId,
                 new AutomaticZenRule.Builder("Rule", CONDITION_ID)
                         // no zen policy
                         .build(),
                 ORIGIN_APP, "reasons", 0);
 
-        AutomaticZenRule savedRule = mZenModeHelper.getAutomaticZenRule(ruleId);
+        AutomaticZenRule savedRule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId);
         assertThat(savedRule.getZenPolicy().getPriorityCategoryCalls())
                 .isEqualTo(STATE_DISALLOW);
     }
@@ -2886,7 +2902,8 @@
         // Test that when updating an automatic zen rule with an existing policy, the newly set
         // fields overwrite those from the previous policy, but unset fields in the new policy
         // keep values from the previous one.
-        String ruleId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(),
+        String ruleId = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT,
+                mContext.getPackageName(),
                 new AutomaticZenRule.Builder("Rule", CONDITION_ID)
                         .setOwner(OWNER)
                         .setZenPolicy(new ZenPolicy.Builder()
@@ -2895,9 +2912,9 @@
                                 .allowReminders(true)
                                 .build())
                         .build(),
-                ZenModeConfig.ORIGIN_SYSTEM, "reasons", 0);
+                ORIGIN_SYSTEM, "reasons", 0);
 
-        mZenModeHelper.updateAutomaticZenRule(ruleId,
+        mZenModeHelper.updateAutomaticZenRule(UserHandle.CURRENT, ruleId,
                 new AutomaticZenRule.Builder("Rule", CONDITION_ID)
                         .setZenPolicy(new ZenPolicy.Builder()
                                 .allowCalls(ZenPolicy.PEOPLE_TYPE_CONTACTS)
@@ -2905,7 +2922,7 @@
                         .build(),
                 ORIGIN_APP, "reasons", 0);
 
-        AutomaticZenRule savedRule = mZenModeHelper.getAutomaticZenRule(ruleId);
+        AutomaticZenRule savedRule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId);
         assertThat(savedRule.getZenPolicy().getPriorityCategoryCalls())
                 .isEqualTo(STATE_ALLOW);  // from update
         assertThat(savedRule.getZenPolicy().getPriorityCallSenders())
@@ -2931,9 +2948,8 @@
         AutomaticZenRule bedtime = new AutomaticZenRule.Builder("Bedtime Mode (TM)", CONDITION_ID)
                 .setType(TYPE_BEDTIME)
                 .build();
-        String bedtimeRuleId = mZenModeHelper.addAutomaticZenRule("pkg", bedtime,
-                ORIGIN_APP,
-                "reason", CUSTOM_PKG_UID);
+        String bedtimeRuleId = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT, "pkg",
+                bedtime, ORIGIN_APP, "reason", CUSTOM_PKG_UID);
 
         assertThat(mZenModeHelper.mConfig.automaticRules.keySet()).containsExactly(bedtimeRuleId);
     }
@@ -2952,9 +2968,8 @@
         AutomaticZenRule bedtime = new AutomaticZenRule.Builder("Bedtime Mode (TM)", CONDITION_ID)
                 .setType(TYPE_BEDTIME)
                 .build();
-        String bedtimeRuleId = mZenModeHelper.addAutomaticZenRule("pkg", bedtime,
-                ORIGIN_APP,
-                "reason", CUSTOM_PKG_UID);
+        String bedtimeRuleId = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT, "pkg",
+                bedtime, ORIGIN_APP, "reason", CUSTOM_PKG_UID);
 
         assertThat(mZenModeHelper.mConfig.automaticRules.keySet()).containsExactly(
                 ZenModeConfig.EVERY_NIGHT_DEFAULT_RULE_ID, bedtimeRuleId);
@@ -2974,9 +2989,8 @@
         AutomaticZenRule bedtime = new AutomaticZenRule.Builder("Bedtime Mode (TM)", CONDITION_ID)
                 .setType(TYPE_BEDTIME)
                 .build();
-        String bedtimeRuleId = mZenModeHelper.addAutomaticZenRule("pkg", bedtime,
-                ORIGIN_APP,
-                "reason", CUSTOM_PKG_UID);
+        String bedtimeRuleId = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT, "pkg",
+                bedtime, ORIGIN_APP, "reason", CUSTOM_PKG_UID);
 
         assertThat(mZenModeHelper.mConfig.automaticRules.keySet()).containsExactly(
                 ZenModeConfig.EVERY_NIGHT_DEFAULT_RULE_ID, bedtimeRuleId);
@@ -2995,16 +3009,16 @@
 
         AutomaticZenRule futureBedtime = new AutomaticZenRule.Builder("Bedtime (?)", CONDITION_ID)
                 .build();
-        String bedtimeRuleId = mZenModeHelper.addAutomaticZenRule(mPkg, futureBedtime,
-                ORIGIN_APP, "reason", CUSTOM_PKG_UID);
+        String bedtimeRuleId = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT, mPkg,
+                futureBedtime, ORIGIN_APP, "reason", CUSTOM_PKG_UID);
         assertThat(mZenModeHelper.mConfig.automaticRules.keySet())
                 .containsExactly(sleepingRule.id, bedtimeRuleId);
 
         AutomaticZenRule bedtime = new AutomaticZenRule.Builder("Bedtime (!)", CONDITION_ID)
                 .setType(TYPE_BEDTIME)
                 .build();
-        mZenModeHelper.updateAutomaticZenRule(bedtimeRuleId, bedtime, ORIGIN_APP, "reason",
-                CUSTOM_PKG_UID);
+        mZenModeHelper.updateAutomaticZenRule(UserHandle.CURRENT, bedtimeRuleId, bedtime,
+                ORIGIN_APP, "reason", CUSTOM_PKG_UID);
 
         assertThat(mZenModeHelper.mConfig.automaticRules.keySet()).containsExactly(bedtimeRuleId);
     }
@@ -3015,16 +3029,16 @@
         setupZenConfig();
 
         // note that caller=null because that's how it comes in from NMS.setZenMode
-        mZenModeHelper.setManualZenMode(ZEN_MODE_IMPORTANT_INTERRUPTIONS, null,
-                ZenModeConfig.ORIGIN_SYSTEM, "", null, SYSTEM_UID);
+        mZenModeHelper.setManualZenMode(UserHandle.CURRENT, ZEN_MODE_IMPORTANT_INTERRUPTIONS, null,
+                ORIGIN_SYSTEM, "", null, SYSTEM_UID);
 
         // confirm that setting zen mode via setManualZenMode changed the zen mode correctly
         assertEquals(ZEN_MODE_IMPORTANT_INTERRUPTIONS, mZenModeHelper.mZenMode);
         assertEquals(true, mZenModeHelper.mConfig.manualRule.allowManualInvocation);
 
         // and also that it works to turn it back off again
-        mZenModeHelper.setManualZenMode(Global.ZEN_MODE_OFF, null, ZenModeConfig.ORIGIN_SYSTEM,
-                "", null, SYSTEM_UID);
+        mZenModeHelper.setManualZenMode(UserHandle.CURRENT, Global.ZEN_MODE_OFF, null,
+                ORIGIN_SYSTEM, "", null, SYSTEM_UID);
 
         assertEquals(Global.ZEN_MODE_OFF, mZenModeHelper.mZenMode);
     }
@@ -3039,22 +3053,21 @@
             AutomaticZenRule activeRule = new AutomaticZenRule.Builder("Test", CONDITION_ID)
                     .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
                     .build();
-            String activeRuleId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(),
-                    activeRule, ORIGIN_APP, "add it", CUSTOM_PKG_UID);
-            mZenModeHelper.setAutomaticZenRuleState(activeRuleId, CONDITION_TRUE,
-                    ORIGIN_APP,
-                    CUSTOM_PKG_UID);
+            String activeRuleId = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT,
+                    mContext.getPackageName(), activeRule, ORIGIN_APP, "add it", CUSTOM_PKG_UID);
+            mZenModeHelper.setAutomaticZenRuleState(UserHandle.CURRENT, activeRuleId,
+                    CONDITION_TRUE, ORIGIN_APP, CUSTOM_PKG_UID);
             AutomaticZenRule inactiveRule = new AutomaticZenRule.Builder("Test", CONDITION_ID)
                     .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
                     .build();
-            String inactiveRuleId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(),
-                    inactiveRule, ORIGIN_APP, "add it", CUSTOM_PKG_UID);
+            String inactiveRuleId = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT,
+                    mContext.getPackageName(), inactiveRule, ORIGIN_APP, "add it", CUSTOM_PKG_UID);
 
             assertWithMessage("Failure for origin " + origin.name())
                     .that(mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_IMPORTANT_INTERRUPTIONS);
 
             // User turns DND off.
-            mZenModeHelper.setManualZenMode(ZEN_MODE_OFF, null, origin.value(),
+            mZenModeHelper.setManualZenMode(UserHandle.CURRENT, ZEN_MODE_OFF, null, origin.value(),
                     "snoozing", "systemui", SYSTEM_UID);
             assertWithMessage("Failure for origin " + origin.name())
                     .that(mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_OFF);
@@ -3078,21 +3091,20 @@
             AutomaticZenRule activeRule = new AutomaticZenRule.Builder("Test", CONDITION_ID)
                     .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
                     .build();
-            String activeRuleId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(),
-                    activeRule, ORIGIN_APP, "add it", CUSTOM_PKG_UID);
-            mZenModeHelper.setAutomaticZenRuleState(activeRuleId, CONDITION_TRUE,
-                    ORIGIN_APP,
-                    CUSTOM_PKG_UID);
+            String activeRuleId = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT,
+                    mContext.getPackageName(), activeRule, ORIGIN_APP, "add it", CUSTOM_PKG_UID);
+            mZenModeHelper.setAutomaticZenRuleState(UserHandle.CURRENT, activeRuleId,
+                    CONDITION_TRUE, ORIGIN_APP, CUSTOM_PKG_UID);
             AutomaticZenRule inactiveRule = new AutomaticZenRule.Builder("Test", CONDITION_ID)
                     .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
                     .build();
-            String inactiveRuleId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(),
-                    inactiveRule, ORIGIN_APP, "add it", CUSTOM_PKG_UID);
+            String inactiveRuleId = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT,
+                    mContext.getPackageName(), inactiveRule, ORIGIN_APP, "add it", CUSTOM_PKG_UID);
 
             assertThat(mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_IMPORTANT_INTERRUPTIONS);
 
             // User turns DND off.
-            mZenModeHelper.setManualZenMode(ZEN_MODE_OFF, null, origin.value(),
+            mZenModeHelper.setManualZenMode(UserHandle.CURRENT, ZEN_MODE_OFF, null, origin.value(),
                     "snoozing", "systemui", SYSTEM_UID);
             ZenModeConfig config = mZenModeHelper.mConfig;
             if (origin == ZenChangeOrigin.ORIGIN_USER_IN_SYSTEMUI) {
@@ -3123,15 +3135,15 @@
         setupZenConfig();
 
         // note that caller=null because that's how it comes in from NMS.setZenMode
-        mZenModeHelper.setManualZenMode(ZEN_MODE_IMPORTANT_INTERRUPTIONS, null,
-                ZenModeConfig.ORIGIN_SYSTEM, "", null, SYSTEM_UID);
+        mZenModeHelper.setManualZenMode(UserHandle.CURRENT, ZEN_MODE_IMPORTANT_INTERRUPTIONS, null,
+                ORIGIN_SYSTEM, "", null, SYSTEM_UID);
 
         // confirm that setting zen mode via setManualZenMode changed the zen mode correctly
         assertEquals(ZEN_MODE_IMPORTANT_INTERRUPTIONS, mZenModeHelper.mZenMode);
 
         // and also that it works to turn it back off again
-        mZenModeHelper.setManualZenMode(ZEN_MODE_OFF, null, ZenModeConfig.ORIGIN_SYSTEM, "",
-                null, SYSTEM_UID);
+        mZenModeHelper.setManualZenMode(UserHandle.CURRENT, ZEN_MODE_OFF, null,
+                ORIGIN_SYSTEM, "", null, SYSTEM_UID);
 
         assertEquals(ZEN_MODE_OFF, mZenModeHelper.mZenMode);
     }
@@ -3144,14 +3156,13 @@
 
         // Turn zen mode on (to important_interruptions)
         // Need to additionally call the looper in order to finish the post-apply-config process
-        mZenModeHelper.setManualZenMode(ZEN_MODE_IMPORTANT_INTERRUPTIONS, null,
-                Flags.modesApi() ? ORIGIN_USER_IN_SYSTEMUI : ZenModeConfig.ORIGIN_SYSTEM, "",
-                null, SYSTEM_UID);
+        mZenModeHelper.setManualZenMode(UserHandle.CURRENT, ZEN_MODE_IMPORTANT_INTERRUPTIONS, null,
+                Flags.modesApi() ? ORIGIN_USER_IN_SYSTEMUI : ORIGIN_SYSTEM, "", null, SYSTEM_UID);
 
         // Now turn zen mode off, but via a different package UID -- this should get registered as
         // "not an action by the user" because some other app is changing zen mode
-        mZenModeHelper.setManualZenMode(ZEN_MODE_OFF, null, ORIGIN_APP, "", null,
-                CUSTOM_PKG_UID);
+        mZenModeHelper.setManualZenMode(UserHandle.CURRENT, ZEN_MODE_OFF, null, ORIGIN_APP, "",
+                null, CUSTOM_PKG_UID);
 
         // In total, this should be 2 loggable changes
         assertEquals(2, mZenModeEventLogger.numLoggedChanges());
@@ -3220,22 +3231,21 @@
                 ZenModeConfig.toScheduleConditionId(new ScheduleInfo()),
                 null,
                 NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
-        String id = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), zenRule,
-                ORIGIN_APP, "test", CUSTOM_PKG_UID);
+        String id = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT,
+                mContext.getPackageName(), zenRule, ORIGIN_APP, "test", CUSTOM_PKG_UID);
 
         // Event 1: Mimic the rule coming on automatically by setting the Condition to STATE_TRUE
         // Note that pre-modes_ui, this event serves as a test that automatic changes to an app's
         // that look like they're coming from the system are attributed to the app, but when
         // modes_ui is true, we opt to trust the provided change origin.
-        mZenModeHelper.setAutomaticZenRuleState(id,
+        mZenModeHelper.setAutomaticZenRuleState(UserHandle.CURRENT, id,
                 new Condition(zenRule.getConditionId(), "", STATE_TRUE),
-                Flags.modesUi() ? ORIGIN_APP : ZenModeConfig.ORIGIN_SYSTEM,
-                CUSTOM_PKG_UID);
+                Flags.modesUi() ? ORIGIN_APP : ORIGIN_SYSTEM, CUSTOM_PKG_UID);
 
         // Event 2: "User" turns off the automatic rule (sets it to not enabled)
         zenRule.setEnabled(false);
-        mZenModeHelper.updateAutomaticZenRule(id, zenRule,
-                Flags.modesApi() ? ORIGIN_USER_IN_SYSTEMUI : ZenModeConfig.ORIGIN_SYSTEM, "",
+        mZenModeHelper.updateAutomaticZenRule(UserHandle.CURRENT, id, zenRule,
+                Flags.modesApi() ? ORIGIN_USER_IN_SYSTEMUI : ORIGIN_SYSTEM, "",
                 SYSTEM_UID);
 
         AutomaticZenRule systemRule = new AutomaticZenRule("systemRule",
@@ -3244,18 +3254,19 @@
                 ZenModeConfig.toScheduleConditionId(new ScheduleInfo()),
                 null,
                 NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
-        String systemId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), systemRule,
-                Flags.modesApi() ? ORIGIN_USER_IN_SYSTEMUI : ZenModeConfig.ORIGIN_SYSTEM, "test",
+        String systemId = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT,
+                mContext.getPackageName(), systemRule,
+                Flags.modesApi() ? ORIGIN_USER_IN_SYSTEMUI : ORIGIN_SYSTEM, "test",
                 SYSTEM_UID);
 
         // Event 3: turn on the system rule
-        mZenModeHelper.setAutomaticZenRuleState(systemId,
+        mZenModeHelper.setAutomaticZenRuleState(UserHandle.CURRENT, systemId,
                 new Condition(zenRule.getConditionId(), "", STATE_TRUE),
-                ZenModeConfig.ORIGIN_SYSTEM, SYSTEM_UID);
+                ORIGIN_SYSTEM, SYSTEM_UID);
 
         // Event 4: "User" deletes the rule
-        mZenModeHelper.removeAutomaticZenRule(systemId,
-                Flags.modesApi() ? ORIGIN_USER_IN_SYSTEMUI : ZenModeConfig.ORIGIN_SYSTEM, "",
+        mZenModeHelper.removeAutomaticZenRule(UserHandle.CURRENT, systemId,
+                Flags.modesApi() ? ORIGIN_USER_IN_SYSTEMUI : ORIGIN_SYSTEM, "",
                 SYSTEM_UID);
         // In total, this represents 4 events
         assertEquals(4, mZenModeEventLogger.numLoggedChanges());
@@ -3311,7 +3322,7 @@
         assertFalse(mZenModeEventLogger.getIsUserAction(2));
         assertEquals(SYSTEM_UID, mZenModeEventLogger.getPackageUid(2));
         assertThat(mZenModeEventLogger.getChangeOrigin(2)).isEqualTo(
-                Flags.modesUi() ? ZenModeConfig.ORIGIN_SYSTEM : 0);
+                Flags.modesUi() ? ORIGIN_SYSTEM : 0);
 
         // When the system rule is deleted, we consider this a user action that turns DND off
         // (again)
@@ -3339,27 +3350,27 @@
                 ZenModeConfig.toScheduleConditionId(new ScheduleInfo()),
                 null,
                 NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
-        String id = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), zenRule,
-                ORIGIN_APP, "test", CUSTOM_PKG_UID);
+        String id = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT,
+                mContext.getPackageName(), zenRule, ORIGIN_APP, "test", CUSTOM_PKG_UID);
 
         // Event 1: Mimic the rule coming on manually when the user turns it on in the app
         // ("Turn on bedtime now" because user goes to bed earlier).
-        mZenModeHelper.setAutomaticZenRuleState(id,
+        mZenModeHelper.setAutomaticZenRuleState(UserHandle.CURRENT, id,
                 new Condition(zenRule.getConditionId(), "", STATE_TRUE, SOURCE_USER_ACTION),
                 ORIGIN_USER_IN_APP, CUSTOM_PKG_UID);
 
         // Event 2: App deactivates the rule automatically (it's 8 AM, bedtime schedule ends)
-        mZenModeHelper.setAutomaticZenRuleState(id,
+        mZenModeHelper.setAutomaticZenRuleState(UserHandle.CURRENT, id,
                 new Condition(zenRule.getConditionId(), "", STATE_FALSE, SOURCE_SCHEDULE),
                 ORIGIN_APP, CUSTOM_PKG_UID);
 
         // Event 3: App activates the rule automatically (it's now 11 PM, bedtime schedule starts)
-        mZenModeHelper.setAutomaticZenRuleState(id,
+        mZenModeHelper.setAutomaticZenRuleState(UserHandle.CURRENT, id,
                 new Condition(zenRule.getConditionId(), "", STATE_TRUE, SOURCE_SCHEDULE),
                 ORIGIN_APP, CUSTOM_PKG_UID);
 
         // Event 4: User deactivates the rule manually (they get up before 8 AM on the next day)
-        mZenModeHelper.setAutomaticZenRuleState(id,
+        mZenModeHelper.setAutomaticZenRuleState(UserHandle.CURRENT, id,
                 new Condition(zenRule.getConditionId(), "", STATE_FALSE, SOURCE_USER_ACTION),
                 ORIGIN_USER_IN_APP, CUSTOM_PKG_UID);
 
@@ -3427,21 +3438,23 @@
         setupZenConfig();
 
         // First just turn zen mode on
-        mZenModeHelper.setManualZenMode(ZEN_MODE_IMPORTANT_INTERRUPTIONS, null,
+        mZenModeHelper.setManualZenMode(UserHandle.CURRENT, ZEN_MODE_IMPORTANT_INTERRUPTIONS, null,
                 ORIGIN_USER_IN_SYSTEMUI, "", null, SYSTEM_UID);
 
         // Now change the policy slightly; want to confirm that this'll be reflected in the logs
         ZenModeConfig newConfig = mZenModeHelper.mConfig.copy();
-        mZenModeHelper.setNotificationPolicy(new Policy(PRIORITY_CATEGORY_ALARMS, 0, 0),
+        mZenModeHelper.setNotificationPolicy(UserHandle.CURRENT,
+                new Policy(PRIORITY_CATEGORY_ALARMS, 0, 0),
                 ORIGIN_USER_IN_SYSTEMUI, SYSTEM_UID);
 
         // Turn zen mode off; we want to make sure policy changes do not get logged when zen mode
         // is off.
-        mZenModeHelper.setManualZenMode(ZEN_MODE_OFF, null, ZenModeConfig.ORIGIN_SYSTEM, "",
-                null, SYSTEM_UID);
+        mZenModeHelper.setManualZenMode(UserHandle.CURRENT, ZEN_MODE_OFF, null,
+                ORIGIN_SYSTEM, "", null, SYSTEM_UID);
 
         // Change the policy again
-        mZenModeHelper.setNotificationPolicy(new Policy(PRIORITY_CATEGORY_REPEAT_CALLERS, 0, 0),
+        mZenModeHelper.setNotificationPolicy(UserHandle.CURRENT,
+                new Policy(PRIORITY_CATEGORY_REPEAT_CALLERS, 0, 0),
                 ORIGIN_USER_IN_SYSTEMUI, SYSTEM_UID);
 
         // Total events: we only expect ones for turning on, changing policy, and turning off
@@ -3485,8 +3498,8 @@
                 ZenModeConfig.toScheduleConditionId(new ScheduleInfo()),
                 null,
                 NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
-        String id = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), zenRule,
-                ZenModeConfig.ORIGIN_SYSTEM, "test", SYSTEM_UID);
+        String id = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT,
+                mContext.getPackageName(), zenRule, ORIGIN_SYSTEM, "test", SYSTEM_UID);
 
         // Rule 2, same as rule 1
         AutomaticZenRule zenRule2 = new AutomaticZenRule("name2",
@@ -3495,8 +3508,8 @@
                 ZenModeConfig.toScheduleConditionId(new ScheduleInfo()),
                 null,
                 NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
-        String id2 = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), zenRule2,
-                ZenModeConfig.ORIGIN_SYSTEM, "test", SYSTEM_UID);
+        String id2 = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT,
+                mContext.getPackageName(), zenRule2, ORIGIN_SYSTEM, "test", SYSTEM_UID);
 
         // Rule 3, has stricter settings than the default settings
         ZenModeConfig ruleConfig = mZenModeHelper.mConfig.copy();
@@ -3507,28 +3520,27 @@
                 ZenModeConfig.toScheduleConditionId(new ScheduleInfo()),
                 ruleConfig.getZenPolicy(),
                 NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
-        String id3 = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), zenRule3,
-                ZenModeConfig.ORIGIN_SYSTEM, "test", SYSTEM_UID);
+        String id3 = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT,
+                mContext.getPackageName(), zenRule3, ORIGIN_SYSTEM, "test", SYSTEM_UID);
 
         // First: turn on rule 1
-        mZenModeHelper.setAutomaticZenRuleState(id,
-                new Condition(zenRule.getConditionId(), "", STATE_TRUE),
-                ZenModeConfig.ORIGIN_SYSTEM, SYSTEM_UID);
+        mZenModeHelper.setAutomaticZenRuleState(UserHandle.CURRENT, id,
+                new Condition(zenRule.getConditionId(), "", STATE_TRUE), ORIGIN_SYSTEM, SYSTEM_UID);
 
         // Second: turn on rule 2
-        mZenModeHelper.setAutomaticZenRuleState(id2,
-                new Condition(zenRule2.getConditionId(), "", STATE_TRUE),
-                ZenModeConfig.ORIGIN_SYSTEM, SYSTEM_UID);
+        mZenModeHelper.setAutomaticZenRuleState(UserHandle.CURRENT, id2,
+                new Condition(zenRule2.getConditionId(), "", STATE_TRUE), ORIGIN_SYSTEM,
+                SYSTEM_UID);
 
         // Third: turn on rule 3
-        mZenModeHelper.setAutomaticZenRuleState(id3,
-                new Condition(zenRule3.getConditionId(), "", STATE_TRUE),
-                ZenModeConfig.ORIGIN_SYSTEM, SYSTEM_UID);
+        mZenModeHelper.setAutomaticZenRuleState(UserHandle.CURRENT, id3,
+                new Condition(zenRule3.getConditionId(), "", STATE_TRUE), ORIGIN_SYSTEM,
+                SYSTEM_UID);
 
         // Fourth: Turn *off* rule 2
-        mZenModeHelper.setAutomaticZenRuleState(id2,
-                new Condition(zenRule2.getConditionId(), "", STATE_FALSE),
-                ZenModeConfig.ORIGIN_SYSTEM, SYSTEM_UID);
+        mZenModeHelper.setAutomaticZenRuleState(UserHandle.CURRENT, id2,
+                new Condition(zenRule2.getConditionId(), "", STATE_FALSE), ORIGIN_SYSTEM,
+                SYSTEM_UID);
 
         // This should result in a total of four events
         assertEquals(4, mZenModeEventLogger.numLoggedChanges());
@@ -3612,8 +3624,8 @@
                 ZenModeConfig.toScheduleConditionId(new ScheduleInfo()),
                 manualRulePolicy,
                 NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
-        String id = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), zenRule,
-                ORIGIN_APP, "test", SYSTEM_UID);
+        String id = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT,
+                mContext.getPackageName(), zenRule, ORIGIN_APP, "test", SYSTEM_UID);
 
         // Rule 2, same as rule 1 but owned by the system
         AutomaticZenRule zenRule2 = new AutomaticZenRule("name2",
@@ -3622,41 +3634,38 @@
                 ZenModeConfig.toScheduleConditionId(new ScheduleInfo()),
                 manualRulePolicy,
                 NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
-        String id2 = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), zenRule2,
-                ORIGIN_USER_IN_SYSTEMUI, "test", SYSTEM_UID);
+        String id2 = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT,
+                mContext.getPackageName(), zenRule2, ORIGIN_USER_IN_SYSTEMUI, "test", SYSTEM_UID);
 
         // Turn on rule 1; call looks like it's from the system. Because setting a condition is
         // typically an automatic (non-user-initiated) action, expect the calling UID to be
         // re-evaluated to the one associated with CUSTOM_PKG_NAME.
         // When modes_ui is true: we expect the change origin to be the source of truth.
-        mZenModeHelper.setAutomaticZenRuleState(id,
+        mZenModeHelper.setAutomaticZenRuleState(UserHandle.CURRENT, id,
                 new Condition(zenRule.getConditionId(), "", STATE_TRUE),
-                Flags.modesUi() ? ORIGIN_APP : ZenModeConfig.ORIGIN_SYSTEM,
-                SYSTEM_UID);
+                Flags.modesUi() ? ORIGIN_APP : ORIGIN_SYSTEM, SYSTEM_UID);
 
         // Second: turn on rule 2. This is a system-owned rule and the UID should not be modified
         // (nor even looked up; the mock PackageManager won't handle "android" as input).
-        mZenModeHelper.setAutomaticZenRuleState(id2,
-                new Condition(zenRule2.getConditionId(), "", STATE_TRUE),
-                ZenModeConfig.ORIGIN_SYSTEM, SYSTEM_UID);
+        mZenModeHelper.setAutomaticZenRuleState(UserHandle.CURRENT, id2,
+                new Condition(zenRule2.getConditionId(), "", STATE_TRUE), ORIGIN_SYSTEM,
+                SYSTEM_UID);
 
         // Disable rule 1. Because this looks like a user action, the UID should not be modified
         // from the system-provided one unless modes_ui is true.
         zenRule.setEnabled(false);
-        mZenModeHelper.updateAutomaticZenRule(id, zenRule,
+        mZenModeHelper.updateAutomaticZenRule(UserHandle.CURRENT, id, zenRule,
                 ORIGIN_USER_IN_SYSTEMUI, "", SYSTEM_UID);
 
         // Add a manual rule. Any manual rule changes should not get calling uids reassigned.
-        mZenModeHelper.setManualZenMode(ZEN_MODE_IMPORTANT_INTERRUPTIONS, null,
-                ORIGIN_APP,
-                "", null, CUSTOM_PKG_UID);
+        mZenModeHelper.setManualZenMode(UserHandle.CURRENT, ZEN_MODE_IMPORTANT_INTERRUPTIONS, null,
+                ORIGIN_APP, "", null, CUSTOM_PKG_UID);
 
         // Change rule 2's condition, but from some other UID. Since it doesn't look like it's from
         // the system, we keep the UID info.
         // Note that this probably shouldn't be able to occur in real scenarios.
-        mZenModeHelper.setAutomaticZenRuleState(id2,
-                new Condition(zenRule2.getConditionId(), "", STATE_FALSE),
-                ORIGIN_APP, 12345);
+        mZenModeHelper.setAutomaticZenRuleState(UserHandle.CURRENT, id2,
+                new Condition(zenRule2.getConditionId(), "", STATE_FALSE), ORIGIN_APP, 12345);
 
         // That was 5 events total
         assertEquals(5, mZenModeEventLogger.numLoggedChanges());
@@ -3712,32 +3721,31 @@
 
         // Turn on zen mode with a manual rule with an enabler set. This should *not* count
         // as a user action, and *should* get its UID reassigned.
-        mZenModeHelper.setManualZenMode(ZEN_MODE_IMPORTANT_INTERRUPTIONS, null,
-                ZenModeConfig.ORIGIN_SYSTEM, "", CUSTOM_PKG_NAME, SYSTEM_UID);
+        mZenModeHelper.setManualZenMode(UserHandle.CURRENT, ZEN_MODE_IMPORTANT_INTERRUPTIONS, null,
+                ORIGIN_SYSTEM, "", CUSTOM_PKG_NAME, SYSTEM_UID);
         assertEquals(1, mZenModeEventLogger.numLoggedChanges());
 
         // Now change apps bypassing to true
         ZenModeConfig newConfig = mZenModeHelper.mConfig.copy();
         newConfig.areChannelsBypassingDnd = true;
-        mZenModeHelper.setNotificationPolicy(newConfig.toNotificationPolicy(),
-                ZenModeConfig.ORIGIN_SYSTEM, SYSTEM_UID);
+        mZenModeHelper.setNotificationPolicy(UserHandle.CURRENT, newConfig.toNotificationPolicy(),
+                ORIGIN_SYSTEM, SYSTEM_UID);
         assertEquals(2, mZenModeEventLogger.numLoggedChanges());
 
         // and then back to false, all without changing anything else
         newConfig.areChannelsBypassingDnd = false;
-        mZenModeHelper.setNotificationPolicy(newConfig.toNotificationPolicy(),
-                ZenModeConfig.ORIGIN_SYSTEM, SYSTEM_UID);
+        mZenModeHelper.setNotificationPolicy(UserHandle.CURRENT, newConfig.toNotificationPolicy(),
+                ORIGIN_SYSTEM, SYSTEM_UID);
         assertEquals(3, mZenModeEventLogger.numLoggedChanges());
 
         // Turn off manual mode, call from a package: don't reset UID even though enabler is set
-        mZenModeHelper.setManualZenMode(ZEN_MODE_OFF, null, ORIGIN_APP, "",
+        mZenModeHelper.setManualZenMode(UserHandle.CURRENT, ZEN_MODE_OFF, null, ORIGIN_APP, "",
                 CUSTOM_PKG_NAME, 12345);
         assertEquals(4, mZenModeEventLogger.numLoggedChanges());
 
         // And likewise when turning it back on again
-        mZenModeHelper.setManualZenMode(ZEN_MODE_IMPORTANT_INTERRUPTIONS, null,
-                ORIGIN_APP,
-                "", CUSTOM_PKG_NAME, 12345);
+        mZenModeHelper.setManualZenMode(UserHandle.CURRENT, ZEN_MODE_IMPORTANT_INTERRUPTIONS, null,
+                ORIGIN_APP, "", CUSTOM_PKG_NAME, 12345);
 
         // These are 5 events in total.
         assertEquals(5, mZenModeEventLogger.numLoggedChanges());
@@ -3782,8 +3790,8 @@
         setupZenConfig();
 
         // First just turn zen mode on
-        mZenModeHelper.setManualZenMode(ZEN_MODE_IMPORTANT_INTERRUPTIONS, null,
-                ZenModeConfig.ORIGIN_SYSTEM, "", null, SYSTEM_UID);
+        mZenModeHelper.setManualZenMode(UserHandle.CURRENT, ZEN_MODE_IMPORTANT_INTERRUPTIONS, null,
+                ORIGIN_SYSTEM, "", null, SYSTEM_UID);
 
         // Now change only the channels part of the policy; want to confirm that this'll be
         // reflected in the logs
@@ -3793,8 +3801,8 @@
                 oldPolicy.priorityMessageSenders, oldPolicy.suppressedVisualEffects,
                 STATE_PRIORITY_CHANNELS_BLOCKED,
                 oldPolicy.priorityConversationSenders);
-        mZenModeHelper.setNotificationPolicy(newPolicy,
-                ZenModeConfig.ORIGIN_SYSTEM, SYSTEM_UID);
+        mZenModeHelper.setNotificationPolicy(UserHandle.CURRENT, newPolicy, ORIGIN_SYSTEM,
+                SYSTEM_UID);
 
         // Total events: one for turning on, one for changing policy
         assertThat(mZenModeEventLogger.numLoggedChanges()).isEqualTo(2);
@@ -3834,16 +3842,16 @@
                 Uri.parse("condition"),
                 null,
                 NotificationManager.INTERRUPTION_FILTER_ALL, true);
-        String id = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), zenRule,
-                ORIGIN_APP, "test", CUSTOM_PKG_UID);
+        String id = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT,
+                mContext.getPackageName(), zenRule, ORIGIN_APP, "test", CUSTOM_PKG_UID);
 
         // Event 1: App activates the rule automatically.
-        mZenModeHelper.setAutomaticZenRuleState(id,
+        mZenModeHelper.setAutomaticZenRuleState(UserHandle.CURRENT, id,
                 new Condition(zenRule.getConditionId(), "", STATE_TRUE, SOURCE_SCHEDULE),
                 ORIGIN_APP, CUSTOM_PKG_UID);
 
         // Event 2: App deactivates the rule automatically.
-        mZenModeHelper.setAutomaticZenRuleState(id,
+        mZenModeHelper.setAutomaticZenRuleState(UserHandle.CURRENT, id,
                 new Condition(zenRule.getConditionId(), "", STATE_FALSE, SOURCE_SCHEDULE),
                 ORIGIN_APP, CUSTOM_PKG_UID);
 
@@ -3876,35 +3884,33 @@
                 .setInterruptionFilter(INTERRUPTION_FILTER_ALL)
                 .setType(TYPE_BEDTIME)
                 .build();
-        String bedtimeRuleId = mZenModeHelper.addAutomaticZenRule(mPkg, bedtime,
-                ORIGIN_APP,
-                "reason", CUSTOM_PKG_UID);
+        String bedtimeRuleId = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT, mPkg, bedtime,
+                ORIGIN_APP, "reason", CUSTOM_PKG_UID);
 
         // Create immersive rule
         AutomaticZenRule immersive = new AutomaticZenRule.Builder("Immersed", CONDITION_ID)
                 .setType(TYPE_IMMERSIVE)
                 .setZenPolicy(mZenModeHelper.mConfig.getZenPolicy()) // same as the manual rule
                 .build();
-        String immersiveId = mZenModeHelper.addAutomaticZenRule(mPkg, immersive,
-                ORIGIN_APP,
-                "reason", CUSTOM_PKG_UID);
+        String immersiveId = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT, mPkg, immersive,
+                ORIGIN_APP, "reason", CUSTOM_PKG_UID);
 
         // Event 1: Activate bedtime rule. This doesn't turn on notification filtering
-        mZenModeHelper.setAutomaticZenRuleState(bedtimeRuleId,
+        mZenModeHelper.setAutomaticZenRuleState(UserHandle.CURRENT, bedtimeRuleId,
                 new Condition(bedtime.getConditionId(), "", STATE_TRUE, SOURCE_SCHEDULE),
                 ORIGIN_APP, CUSTOM_PKG_UID);
 
         // Event 2: turn on manual zen mode. Manual rule will have ACTIVE_RULE_TYPE_MANUAL
-        mZenModeHelper.setManualZenMode(ZEN_MODE_IMPORTANT_INTERRUPTIONS, null,
-                ZenModeConfig.ORIGIN_SYSTEM, "", null, SYSTEM_UID);
+        mZenModeHelper.setManualZenMode(UserHandle.CURRENT, ZEN_MODE_IMPORTANT_INTERRUPTIONS, null,
+                ORIGIN_SYSTEM, "", null, SYSTEM_UID);
 
         // Event 3: Turn immersive on
-        mZenModeHelper.setAutomaticZenRuleState(immersiveId,
+        mZenModeHelper.setAutomaticZenRuleState(UserHandle.CURRENT, immersiveId,
                 new Condition(immersive.getConditionId(), "", STATE_TRUE, SOURCE_SCHEDULE),
                 ORIGIN_APP, CUSTOM_PKG_UID);
 
         // Event 4: Turn off bedtime mode, leaving just manual + immersive
-        mZenModeHelper.setAutomaticZenRuleState(bedtimeRuleId,
+        mZenModeHelper.setAutomaticZenRuleState(UserHandle.CURRENT, bedtimeRuleId,
                 new Condition(bedtime.getConditionId(), "", STATE_FALSE, SOURCE_SCHEDULE),
                 ORIGIN_APP, CUSTOM_PKG_UID);
 
@@ -3966,15 +3972,15 @@
                 ZenModeConfig.toScheduleConditionId(new ScheduleInfo()),
                 null,
                 NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
-        String id = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), zenRule,
-                ZenModeConfig.ORIGIN_SYSTEM, "test", SYSTEM_UID);
+        String id = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT,
+                mContext.getPackageName(), zenRule, ORIGIN_SYSTEM, "test", SYSTEM_UID);
 
         // enable the rule
-        mZenModeHelper.setAutomaticZenRuleState(id,
+        mZenModeHelper.setAutomaticZenRuleState(UserHandle.CURRENT, id,
                 new Condition(zenRule.getConditionId(), "", STATE_TRUE),
-                ZenModeConfig.ORIGIN_SYSTEM, SYSTEM_UID);
+                ORIGIN_SYSTEM, SYSTEM_UID);
 
-        assertEquals(mZenModeHelper.getNotificationPolicy(),
+        assertEquals(mZenModeHelper.getNotificationPolicy(UserHandle.CURRENT),
                 mZenModeHelper.getConsolidatedNotificationPolicy());
 
         // inspect the consolidated policy. Based on setupZenConfig() values.
@@ -4002,13 +4008,13 @@
                 ZenModeConfig.toScheduleConditionId(new ScheduleInfo()),
                 null,  // null policy
                 NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
-        String id = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), zenRule,
-                ZenModeConfig.ORIGIN_SYSTEM, "test", SYSTEM_UID);
+        String id = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT,
+                mContext.getPackageName(), zenRule, ORIGIN_SYSTEM, "test", SYSTEM_UID);
 
         // enable the rule
-        mZenModeHelper.setAutomaticZenRuleState(id,
+        mZenModeHelper.setAutomaticZenRuleState(UserHandle.CURRENT, id,
                 new Condition(zenRule.getConditionId(), "", STATE_TRUE),
-                ZenModeConfig.ORIGIN_SYSTEM, SYSTEM_UID);
+                ORIGIN_SYSTEM, SYSTEM_UID);
 
         // inspect the consolidated policy, which should match the device default settings.
         assertThat(ZenAdapters.notificationPolicyToZenPolicy(mZenModeHelper.mConsolidatedPolicy))
@@ -4040,13 +4046,12 @@
                 ZenModeConfig.toScheduleConditionId(new ScheduleInfo()),
                 customPolicy,
                 NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
-        String id = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), zenRule,
-                ZenModeConfig.ORIGIN_SYSTEM, "test", SYSTEM_UID);
+        String id = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT,
+                mContext.getPackageName(), zenRule, ORIGIN_SYSTEM, "test", SYSTEM_UID);
 
         // enable the rule; this will update the consolidated policy
-        mZenModeHelper.setAutomaticZenRuleState(id,
-                new Condition(zenRule.getConditionId(), "", STATE_TRUE),
-                ZenModeConfig.ORIGIN_SYSTEM, SYSTEM_UID);
+        mZenModeHelper.setAutomaticZenRuleState(UserHandle.CURRENT, id,
+                new Condition(zenRule.getConditionId(), "", STATE_TRUE), ORIGIN_SYSTEM, SYSTEM_UID);
 
         // since this is the only active rule, the consolidated policy should match the custom
         // policy for every field specified, and take default values (from device default or
@@ -4085,13 +4090,12 @@
                 ZenModeConfig.toScheduleConditionId(new ScheduleInfo()),
                 customPolicy,
                 NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
-        String id = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), zenRule,
-                ZenModeConfig.ORIGIN_SYSTEM, "test", SYSTEM_UID);
+        String id = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT,
+                mContext.getPackageName(), zenRule, ORIGIN_SYSTEM, "test", SYSTEM_UID);
 
         // enable the rule; this will update the consolidated policy
-        mZenModeHelper.setAutomaticZenRuleState(id,
-                new Condition(zenRule.getConditionId(), "", STATE_TRUE),
-                ZenModeConfig.ORIGIN_SYSTEM, SYSTEM_UID);
+        mZenModeHelper.setAutomaticZenRuleState(UserHandle.CURRENT, id,
+                new Condition(zenRule.getConditionId(), "", STATE_TRUE), ORIGIN_SYSTEM, SYSTEM_UID);
 
         // since this is the only active rule, the consolidated policy should match the custom
         // policy for every field specified, and take default values (from either device default
@@ -4125,13 +4129,12 @@
                 ZenModeConfig.toScheduleConditionId(new ScheduleInfo()),
                 null,
                 NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
-        String id = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), zenRule,
-                ZenModeConfig.ORIGIN_SYSTEM, "test", SYSTEM_UID);
+        String id = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT,
+                mContext.getPackageName(), zenRule, ORIGIN_SYSTEM, "test", SYSTEM_UID);
 
         // enable rule 1
-        mZenModeHelper.setAutomaticZenRuleState(id,
-                new Condition(zenRule.getConditionId(), "", STATE_TRUE),
-                ZenModeConfig.ORIGIN_SYSTEM, SYSTEM_UID);
+        mZenModeHelper.setAutomaticZenRuleState(UserHandle.CURRENT, id,
+                new Condition(zenRule.getConditionId(), "", STATE_TRUE), ORIGIN_SYSTEM, SYSTEM_UID);
 
         // custom policy for rule 2
         ZenPolicy customPolicy = new ZenPolicy.Builder()
@@ -4149,13 +4152,13 @@
                 ZenModeConfig.toScheduleConditionId(new ScheduleInfo()),
                 customPolicy,
                 NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
-        String id2 = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), zenRule2,
-                ZenModeConfig.ORIGIN_SYSTEM, "test", SYSTEM_UID);
+        String id2 = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT,
+                mContext.getPackageName(), zenRule2, ORIGIN_SYSTEM, "test", SYSTEM_UID);
 
         // enable rule 2; this will update the consolidated policy
-        mZenModeHelper.setAutomaticZenRuleState(id2,
+        mZenModeHelper.setAutomaticZenRuleState(UserHandle.CURRENT, id2,
                 new Condition(zenRule2.getConditionId(), "", STATE_TRUE),
-                ZenModeConfig.ORIGIN_SYSTEM, SYSTEM_UID);
+                ORIGIN_SYSTEM, SYSTEM_UID);
 
         // now both rules should be on, and the consolidated policy should reflect the most
         // restrictive option of each of the two
@@ -4186,13 +4189,12 @@
                 ZenModeConfig.toScheduleConditionId(new ScheduleInfo()),
                 null,
                 NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
-        String id = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), zenRule,
-                ZenModeConfig.ORIGIN_SYSTEM, "test", SYSTEM_UID);
+        String id = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT,
+                mContext.getPackageName(), zenRule, ORIGIN_SYSTEM, "test", SYSTEM_UID);
 
         // enable rule 1
-        mZenModeHelper.setAutomaticZenRuleState(id,
-                new Condition(zenRule.getConditionId(), "", STATE_TRUE),
-                ZenModeConfig.ORIGIN_SYSTEM, SYSTEM_UID);
+        mZenModeHelper.setAutomaticZenRuleState(UserHandle.CURRENT, id,
+                new Condition(zenRule.getConditionId(), "", STATE_TRUE), ORIGIN_SYSTEM, SYSTEM_UID);
 
         // custom policy for rule 2
         ZenPolicy customPolicy = new ZenPolicy.Builder()
@@ -4210,13 +4212,13 @@
                 ZenModeConfig.toScheduleConditionId(new ScheduleInfo()),
                 customPolicy,
                 NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
-        String id2 = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), zenRule2,
-                ZenModeConfig.ORIGIN_SYSTEM, "test", SYSTEM_UID);
+        String id2 = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT,
+                mContext.getPackageName(), zenRule2, ORIGIN_SYSTEM, "test", SYSTEM_UID);
 
         // enable rule 2; this will update the consolidated policy
-        mZenModeHelper.setAutomaticZenRuleState(id2,
-                new Condition(zenRule2.getConditionId(), "", STATE_TRUE),
-                ZenModeConfig.ORIGIN_SYSTEM, SYSTEM_UID);
+        mZenModeHelper.setAutomaticZenRuleState(UserHandle.CURRENT, id2,
+                new Condition(zenRule2.getConditionId(), "", STATE_TRUE), ORIGIN_SYSTEM,
+                SYSTEM_UID);
 
         // now both rules should be on, and the consolidated policy should reflect the most
         // restrictive option of each of the two
@@ -4252,13 +4254,12 @@
                 ZenModeConfig.toScheduleConditionId(new ScheduleInfo()),
                 customPolicy,
                 NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
-        String id = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), zenRule,
-                ZenModeConfig.ORIGIN_SYSTEM, "test", SYSTEM_UID);
+        String id = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT,
+                mContext.getPackageName(), zenRule, ORIGIN_SYSTEM, "test", SYSTEM_UID);
 
         // enable the rule; this will update the consolidated policy
-        mZenModeHelper.setAutomaticZenRuleState(id,
-                new Condition(zenRule.getConditionId(), "", STATE_TRUE),
-                ZenModeConfig.ORIGIN_SYSTEM, SYSTEM_UID);
+        mZenModeHelper.setAutomaticZenRuleState(UserHandle.CURRENT, id,
+                new Condition(zenRule.getConditionId(), "", STATE_TRUE), ORIGIN_SYSTEM, SYSTEM_UID);
 
         // confirm that channels make it through
         assertTrue(mZenModeHelper.mConsolidatedPolicy.allowPriorityChannels());
@@ -4274,13 +4275,13 @@
                 ZenModeConfig.toScheduleConditionId(new ScheduleInfo()),
                 strictPolicy,
                 NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
-        String id2 = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), zenRule2,
-                ZenModeConfig.ORIGIN_SYSTEM, "test", SYSTEM_UID);
+        String id2 = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT,
+                mContext.getPackageName(), zenRule2, ORIGIN_SYSTEM, "test", SYSTEM_UID);
 
         // enable rule 2; this will update the consolidated policy
-        mZenModeHelper.setAutomaticZenRuleState(id2,
-                new Condition(zenRule2.getConditionId(), "", STATE_TRUE),
-                ZenModeConfig.ORIGIN_SYSTEM, SYSTEM_UID);
+        mZenModeHelper.setAutomaticZenRuleState(UserHandle.CURRENT, id2,
+                new Condition(zenRule2.getConditionId(), "", STATE_TRUE), ORIGIN_SYSTEM,
+                SYSTEM_UID);
 
         // rule 2 should override rule 1
         assertFalse(mZenModeHelper.mConsolidatedPolicy.allowPriorityChannels());
@@ -4305,9 +4306,9 @@
                         .allowSystem(true)
                         .build(),
                 NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
-        String rule1Id = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(),
-                zenRuleWithPriority, ORIGIN_APP, "test", CUSTOM_PKG_UID);
-        mZenModeHelper.setAutomaticZenRuleState(rule1Id,
+        String rule1Id = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT,
+                mContext.getPackageName(), zenRuleWithPriority, ORIGIN_APP, "test", CUSTOM_PKG_UID);
+        mZenModeHelper.setAutomaticZenRuleState(UserHandle.CURRENT, rule1Id,
                 new Condition(zenRuleWithPriority.getConditionId(), "", STATE_TRUE),
                 ORIGIN_APP, CUSTOM_PKG_UID);
 
@@ -4318,9 +4319,9 @@
                 Uri.parse("priority"),
                 new ZenPolicy.Builder().disallowAllSounds().build(),
                 NotificationManager.INTERRUPTION_FILTER_ALL, true);
-        String rule2Id = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(),
-                zenRuleWithAll, ORIGIN_APP, "test", CUSTOM_PKG_UID);
-        mZenModeHelper.setAutomaticZenRuleState(rule2Id,
+        String rule2Id = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT,
+                mContext.getPackageName(), zenRuleWithAll, ORIGIN_APP, "test", CUSTOM_PKG_UID);
+        mZenModeHelper.setAutomaticZenRuleState(UserHandle.CURRENT, rule2Id,
                 new Condition(zenRuleWithPriority.getConditionId(), "", STATE_TRUE),
                 ORIGIN_APP, CUSTOM_PKG_UID);
 
@@ -4364,7 +4365,7 @@
         rule.triggerDescription = TRIGGER_DESC;
 
         mZenModeHelper.mConfig.automaticRules.put(rule.id, rule);
-        AutomaticZenRule actual = mZenModeHelper.getAutomaticZenRule(rule.id);
+        AutomaticZenRule actual = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, rule.id);
 
         assertEquals(NAME, actual.getName());
         assertEquals(OWNER, actual.getOwner());
@@ -4400,8 +4401,8 @@
                 .setManualInvocationAllowed(ALLOW_MANUAL)
                 .build();
 
-        String ruleId = mZenModeHelper.addAutomaticZenRule(OWNER.getPackageName(), azr,
-                ORIGIN_APP, "add", CUSTOM_PKG_UID);
+        String ruleId = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT,
+                OWNER.getPackageName(), azr, ORIGIN_APP, "add", CUSTOM_PKG_UID);
 
         ZenModeConfig.ZenRule storedRule = mZenModeHelper.mConfig.automaticRules.get(ruleId);
 
@@ -4430,17 +4431,17 @@
         AutomaticZenRule azrBase = new AutomaticZenRule.Builder("OriginalName", CONDITION_ID)
                 .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
                 .build();
-        String ruleId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(),
-                azrBase, ORIGIN_APP, "reason", SYSTEM_UID);
-        AutomaticZenRule rule = mZenModeHelper.getAutomaticZenRule(ruleId);
+        String ruleId = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT,
+                mContext.getPackageName(), azrBase, ORIGIN_APP, "reason", SYSTEM_UID);
+        AutomaticZenRule rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId);
 
         // Checks the name can be changed by the app because the user has not modified it.
         AutomaticZenRule azrUpdate = new AutomaticZenRule.Builder(rule)
                 .setName("NewName")
                 .build();
-        mZenModeHelper.updateAutomaticZenRule(ruleId, azrUpdate, ORIGIN_APP, "reason",
-                SYSTEM_UID);
-        rule = mZenModeHelper.getAutomaticZenRule(ruleId);
+        mZenModeHelper.updateAutomaticZenRule(UserHandle.CURRENT, ruleId, azrUpdate, ORIGIN_APP,
+                "reason", SYSTEM_UID);
+        rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId);
         assertThat(rule.getName()).isEqualTo("NewName");
 
         // The user modifies some other field in the rule, which makes the rule as a whole not
@@ -4448,35 +4449,35 @@
         azrUpdate = new AutomaticZenRule.Builder(rule)
                 .setInterruptionFilter(INTERRUPTION_FILTER_ALARMS)
                 .build();
-        mZenModeHelper.updateAutomaticZenRule(ruleId, azrUpdate, ORIGIN_USER_IN_SYSTEMUI, "reason",
-                SYSTEM_UID);
+        mZenModeHelper.updateAutomaticZenRule(UserHandle.CURRENT, ruleId, azrUpdate,
+                ORIGIN_USER_IN_SYSTEMUI, "reason", SYSTEM_UID);
 
         // ...but the app can still modify the name, because the name itself hasn't been modified
         // by the user.
         azrUpdate = new AutomaticZenRule.Builder(rule)
                 .setName("NewAppName")
                 .build();
-        mZenModeHelper.updateAutomaticZenRule(ruleId, azrUpdate, ORIGIN_APP, "reason",
-                SYSTEM_UID);
-        rule = mZenModeHelper.getAutomaticZenRule(ruleId);
+        mZenModeHelper.updateAutomaticZenRule(UserHandle.CURRENT, ruleId, azrUpdate, ORIGIN_APP,
+                "reason", SYSTEM_UID);
+        rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId);
         assertThat(rule.getName()).isEqualTo("NewAppName");
 
         // The user modifies the name.
         azrUpdate = new AutomaticZenRule.Builder(rule)
                 .setName("UserProvidedName")
                 .build();
-        mZenModeHelper.updateAutomaticZenRule(ruleId, azrUpdate, ORIGIN_USER_IN_SYSTEMUI, "reason",
-                SYSTEM_UID);
-        rule = mZenModeHelper.getAutomaticZenRule(ruleId);
+        mZenModeHelper.updateAutomaticZenRule(UserHandle.CURRENT, ruleId, azrUpdate,
+                ORIGIN_USER_IN_SYSTEMUI, "reason", SYSTEM_UID);
+        rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId);
         assertThat(rule.getName()).isEqualTo("UserProvidedName");
 
         // The app is no longer able to modify the name.
         azrUpdate = new AutomaticZenRule.Builder(rule)
                 .setName("NewAppName")
                 .build();
-        mZenModeHelper.updateAutomaticZenRule(ruleId, azrUpdate, ORIGIN_APP, "reason",
-                SYSTEM_UID);
-        rule = mZenModeHelper.getAutomaticZenRule(ruleId);
+        mZenModeHelper.updateAutomaticZenRule(UserHandle.CURRENT, ruleId, azrUpdate, ORIGIN_APP,
+                "reason", SYSTEM_UID);
+        rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId);
         assertThat(rule.getName()).isEqualTo("UserProvidedName");
     }
 
@@ -4490,9 +4491,9 @@
                 .setDeviceEffects(new ZenDeviceEffects.Builder().build())
                 .build();
         // Adds the rule using the app, to avoid having any user modified bits set.
-        String ruleId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(),
-                azrBase, ORIGIN_APP, "reason", SYSTEM_UID);
-        AutomaticZenRule rule = mZenModeHelper.getAutomaticZenRule(ruleId);
+        String ruleId = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT,
+                mContext.getPackageName(), azrBase, ORIGIN_APP, "reason", SYSTEM_UID);
+        AutomaticZenRule rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId);
 
         // Modifies the filter, icon, zen policy, and device effects
         ZenPolicy policy = new ZenPolicy.Builder(rule.getZenPolicy())
@@ -4510,9 +4511,9 @@
                 .build();
 
         // Update the rule with the AZR from origin user.
-        mZenModeHelper.updateAutomaticZenRule(ruleId, azrUpdate, ORIGIN_USER_IN_SYSTEMUI, "reason",
-                SYSTEM_UID);
-        rule = mZenModeHelper.getAutomaticZenRule(ruleId);
+        mZenModeHelper.updateAutomaticZenRule(UserHandle.CURRENT, ruleId, azrUpdate,
+                ORIGIN_USER_IN_SYSTEMUI, "reason", SYSTEM_UID);
+        rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId);
 
         // UPDATE_ORIGIN_USER should change the bitmask and change the values.
         assertThat(rule.getInterruptionFilter()).isEqualTo(INTERRUPTION_FILTER_PRIORITY);
@@ -4547,9 +4548,9 @@
                         .build())
                 .build();
         // Adds the rule using the app, to avoid having any user modified bits set.
-        String ruleId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(),
-                azrBase, ORIGIN_APP, "reason", SYSTEM_UID);
-        AutomaticZenRule rule = mZenModeHelper.getAutomaticZenRule(ruleId);
+        String ruleId = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT,
+                mContext.getPackageName(), azrBase, ORIGIN_APP, "reason", SYSTEM_UID);
+        AutomaticZenRule rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId);
 
         // Modifies the icon, zen policy and device effects
         ZenPolicy policy = new ZenPolicy.Builder(rule.getZenPolicy())
@@ -4567,9 +4568,9 @@
                 .build();
 
         // Update the rule with the AZR from origin systemUI.
-        mZenModeHelper.updateAutomaticZenRule(ruleId, azrUpdate, ZenModeConfig.ORIGIN_SYSTEM,
+        mZenModeHelper.updateAutomaticZenRule(UserHandle.CURRENT, ruleId, azrUpdate, ORIGIN_SYSTEM,
                 "reason", SYSTEM_UID);
-        rule = mZenModeHelper.getAutomaticZenRule(ruleId);
+        rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId);
 
         // UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI should change the value but NOT update the bitmask.
         assertThat(rule.getIconResId()).isEqualTo(ICON_RES_ID);
@@ -4597,9 +4598,9 @@
                         .build())
                 .build();
         // Adds the rule using the app, to avoid having any user modified bits set.
-        String ruleId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(),
-                azrBase, ORIGIN_APP, "reason", SYSTEM_UID);
-        AutomaticZenRule rule = mZenModeHelper.getAutomaticZenRule(ruleId);
+        String ruleId = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT,
+                mContext.getPackageName(), azrBase, ORIGIN_APP, "reason", SYSTEM_UID);
+        AutomaticZenRule rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId);
 
         ZenPolicy policy = new ZenPolicy.Builder()
                 .allowReminders(true)
@@ -4615,8 +4616,8 @@
 
         // Since the rule is not already user modified, UPDATE_ORIGIN_APP can modify the rule.
         // The bitmask is not modified.
-        mZenModeHelper.updateAutomaticZenRule(ruleId, azrUpdate, ORIGIN_APP, "reason",
-                SYSTEM_UID);
+        mZenModeHelper.updateAutomaticZenRule(UserHandle.CURRENT, ruleId, azrUpdate, ORIGIN_APP,
+                "reason", SYSTEM_UID);
 
         ZenRule storedRule = mZenModeHelper.mConfig.automaticRules.get(ruleId);
         assertThat(storedRule.userModifiedFields).isEqualTo(0);
@@ -4630,8 +4631,8 @@
         assertThat(storedRule.zenDeviceEffectsUserModifiedFields).isEqualTo(0);
 
         // Creates another rule, this time from user. This will have user modified bits set.
-        String ruleIdUser = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(),
-                azrBase, ORIGIN_USER_IN_SYSTEMUI, "reason", SYSTEM_UID);
+        String ruleIdUser = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT,
+                mContext.getPackageName(), azrBase, ORIGIN_USER_IN_SYSTEMUI, "reason", SYSTEM_UID);
         storedRule = mZenModeHelper.mConfig.automaticRules.get(ruleIdUser);
         int ruleModifiedFields = storedRule.userModifiedFields;
         int rulePolicyModifiedFields = storedRule.zenPolicyUserModifiedFields;
@@ -4639,9 +4640,10 @@
 
         // Zen rule update coming from the app again. This cannot fully update the rule, because
         // the rule is already considered user modified.
-        mZenModeHelper.updateAutomaticZenRule(ruleIdUser, azrUpdate, ORIGIN_APP,
+        mZenModeHelper.updateAutomaticZenRule(UserHandle.CURRENT, ruleIdUser, azrUpdate, ORIGIN_APP,
                 "reason", SYSTEM_UID);
-        AutomaticZenRule ruleUser = mZenModeHelper.getAutomaticZenRule(ruleIdUser);
+        AutomaticZenRule ruleUser = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT,
+                ruleIdUser);
 
         // The app can only change the value if the rule is not already user modified,
         // so the rule is not changed, and neither is the bitmask.
@@ -4670,9 +4672,9 @@
                         .setShouldDisplayGrayscale(true)
                         .build())
                 .build();
-        String ruleId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(),
-                azrBase, ORIGIN_APP, "reason", SYSTEM_UID);
-        AutomaticZenRule rule = mZenModeHelper.getAutomaticZenRule(ruleId);
+        String ruleId = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT,
+                mContext.getPackageName(), azrBase, ORIGIN_APP, "reason", SYSTEM_UID);
+        AutomaticZenRule rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId);
 
         // The values are modified but the bitmask is not.
         assertThat(rule.getZenPolicy().getPriorityCategoryReminders())
@@ -4692,8 +4694,8 @@
                 .setDeviceEffects(zde)
                 .build();
         // Adds the rule using the app, to avoid having any user modified bits set.
-        String ruleId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(),
-                azrBase, ORIGIN_APP, "reason", SYSTEM_UID);
+        String ruleId = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT,
+                mContext.getPackageName(), azrBase, ORIGIN_APP, "reason", SYSTEM_UID);
 
         AutomaticZenRule azr = new AutomaticZenRule.Builder(azrBase)
                 // Sets Device Effects to null
@@ -4702,9 +4704,9 @@
 
         // Zen rule update coming from app, but since the rule isn't already
         // user modified, it can be updated.
-        mZenModeHelper.updateAutomaticZenRule(ruleId, azr, ORIGIN_APP, "reason",
+        mZenModeHelper.updateAutomaticZenRule(UserHandle.CURRENT, ruleId, azr, ORIGIN_APP, "reason",
                 SYSTEM_UID);
-        AutomaticZenRule rule = mZenModeHelper.getAutomaticZenRule(ruleId);
+        AutomaticZenRule rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId);
 
         // When AZR's ZenDeviceEffects is null, the updated rule's device effects are kept.
         assertThat(rule.getDeviceEffects()).isEqualTo(zde);
@@ -4718,9 +4720,9 @@
                 .setZenPolicy(POLICY)
                 .build();
         // Adds the rule using the app, to avoid having any user modified bits set.
-        String ruleId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(),
-                azrBase, ORIGIN_APP, "reason", SYSTEM_UID);
-        AutomaticZenRule rule = mZenModeHelper.getAutomaticZenRule(ruleId);
+        String ruleId = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT,
+                mContext.getPackageName(), azrBase, ORIGIN_APP, "reason", SYSTEM_UID);
+        AutomaticZenRule rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId);
 
         AutomaticZenRule azr = new AutomaticZenRule.Builder(azrBase)
                 // Set zen policy to null
@@ -4729,9 +4731,9 @@
 
         // Zen rule update coming from app, but since the rule isn't already
         // user modified, it can be updated.
-        mZenModeHelper.updateAutomaticZenRule(ruleId, azr, ORIGIN_APP, "reason",
+        mZenModeHelper.updateAutomaticZenRule(UserHandle.CURRENT, ruleId, azr, ORIGIN_APP, "reason",
                 SYSTEM_UID);
-        rule = mZenModeHelper.getAutomaticZenRule(ruleId);
+        rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId);
 
         // When AZR's ZenPolicy is null, we expect the updated rule's policy to be unchanged
         // (equivalent to the provided policy, with additional fields filled in with defaults).
@@ -4750,9 +4752,9 @@
                 // .setDeviceEffects(new ZenDeviceEffects.Builder().build())
                 .build();
         // Adds the rule using the app, to avoid having any user modified bits set.
-        String ruleId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(),
-                azrBase, ORIGIN_APP, "reason", SYSTEM_UID);
-        AutomaticZenRule rule = mZenModeHelper.getAutomaticZenRule(ruleId);
+        String ruleId = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT,
+                mContext.getPackageName(), azrBase, ORIGIN_APP, "reason", SYSTEM_UID);
+        AutomaticZenRule rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId);
 
         // Create a fully populated ZenPolicy.
         ZenPolicy policy = new ZenPolicy.Builder()
@@ -4780,9 +4782,9 @@
 
         // Applies the update to the rule.
         // Default config defined in getDefaultConfigParser() is used as the original rule.
-        mZenModeHelper.updateAutomaticZenRule(ruleId, azr, ORIGIN_USER_IN_SYSTEMUI, "reason",
-                SYSTEM_UID);
-        rule = mZenModeHelper.getAutomaticZenRule(ruleId);
+        mZenModeHelper.updateAutomaticZenRule(UserHandle.CURRENT, ruleId, azr,
+                ORIGIN_USER_IN_SYSTEMUI, "reason", SYSTEM_UID);
+        rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId);
 
         // New ZenPolicy differs from the default config
         assertThat(rule.getZenPolicy()).isNotNull();
@@ -4811,9 +4813,9 @@
                 .setDeviceEffects(null)
                 .build();
         // Adds the rule using the app, to avoid having any user modified bits set.
-        String ruleId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(),
-                azrBase, ORIGIN_APP, "reason", SYSTEM_UID);
-        AutomaticZenRule rule = mZenModeHelper.getAutomaticZenRule(ruleId);
+        String ruleId = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT,
+                mContext.getPackageName(), azrBase, ORIGIN_APP, "reason", SYSTEM_UID);
+        AutomaticZenRule rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId);
 
         ZenDeviceEffects deviceEffects = new ZenDeviceEffects.Builder()
                 .setShouldDisplayGrayscale(true)
@@ -4823,9 +4825,9 @@
                 .build();
 
         // Applies the update to the rule.
-        mZenModeHelper.updateAutomaticZenRule(ruleId, azr, ORIGIN_USER_IN_SYSTEMUI, "reason",
-                SYSTEM_UID);
-        rule = mZenModeHelper.getAutomaticZenRule(ruleId);
+        mZenModeHelper.updateAutomaticZenRule(UserHandle.CURRENT, ruleId, azr,
+                ORIGIN_USER_IN_SYSTEMUI, "reason", SYSTEM_UID);
+        rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId);
 
         // New ZenDeviceEffects is used; all fields considered set, since previously were null.
         assertThat(rule.getDeviceEffects()).isNotNull();
@@ -4848,8 +4850,8 @@
                 ZenModeConfig.toScheduleConditionId(new ScheduleInfo()),
                 null,
                 NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
-        final String createdId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(),
-                zenRule, ZenModeConfig.ORIGIN_SYSTEM, "test", SYSTEM_UID);
+        final String createdId = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT,
+                mContext.getPackageName(), zenRule, ORIGIN_SYSTEM, "test", SYSTEM_UID);
 
         CountDownLatch latch = new CountDownLatch(1);
         final int[] actualStatus = new int[1];
@@ -4865,7 +4867,7 @@
         mZenModeHelper.addCallback(callback);
 
         zenRule.setEnabled(false);
-        mZenModeHelper.updateAutomaticZenRule(createdId, zenRule, ZenModeConfig.ORIGIN_SYSTEM,
+        mZenModeHelper.updateAutomaticZenRule(UserHandle.CURRENT, createdId, zenRule, ORIGIN_SYSTEM,
                 "", SYSTEM_UID);
 
         assertTrue(latch.await(500, TimeUnit.MILLISECONDS));
@@ -4883,8 +4885,8 @@
                 ZenModeConfig.toScheduleConditionId(new ScheduleInfo()),
                 null,
                 NotificationManager.INTERRUPTION_FILTER_PRIORITY, false);
-        final String createdId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(),
-                zenRule, ZenModeConfig.ORIGIN_SYSTEM, "test", SYSTEM_UID);
+        final String createdId = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT,
+                mContext.getPackageName(), zenRule, ORIGIN_SYSTEM, "test", SYSTEM_UID);
 
         CountDownLatch latch = new CountDownLatch(1);
         final int[] actualStatus = new int[1];
@@ -4900,7 +4902,7 @@
         mZenModeHelper.addCallback(callback);
 
         zenRule.setEnabled(true);
-        mZenModeHelper.updateAutomaticZenRule(createdId, zenRule, ZenModeConfig.ORIGIN_SYSTEM,
+        mZenModeHelper.updateAutomaticZenRule(UserHandle.CURRENT, createdId, zenRule, ORIGIN_SYSTEM,
                 "", SYSTEM_UID);
 
         assertTrue(latch.await(500, TimeUnit.MILLISECONDS));
@@ -4919,8 +4921,8 @@
                 ZenModeConfig.toScheduleConditionId(new ScheduleInfo()),
                 null,
                 NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
-        final String createdId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(),
-                zenRule, ZenModeConfig.ORIGIN_SYSTEM, "test", SYSTEM_UID);
+        final String createdId = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT,
+                mContext.getPackageName(), zenRule, ORIGIN_SYSTEM, "test", SYSTEM_UID);
 
         CountDownLatch latch = new CountDownLatch(1);
         final int[] actualStatus = new int[1];
@@ -4935,9 +4937,9 @@
         };
         mZenModeHelper.addCallback(callback);
 
-        mZenModeHelper.setAutomaticZenRuleState(createdId,
+        mZenModeHelper.setAutomaticZenRuleState(UserHandle.CURRENT, createdId,
                 new Condition(zenRule.getConditionId(), "", STATE_TRUE),
-                ZenModeConfig.ORIGIN_SYSTEM, SYSTEM_UID);
+                ORIGIN_SYSTEM, SYSTEM_UID);
 
         assertTrue(latch.await(500, TimeUnit.MILLISECONDS));
         if (CompatChanges.isChangeEnabled(ZenModeHelper.SEND_ACTIVATION_AZR_STATUSES)) {
@@ -4959,8 +4961,8 @@
                 ZenModeConfig.toScheduleConditionId(new ScheduleInfo()),
                 null,
                 NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
-        final String createdId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(),
-                zenRule, ZenModeConfig.ORIGIN_SYSTEM, "test", SYSTEM_UID);
+        final String createdId = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT,
+                mContext.getPackageName(), zenRule, ORIGIN_SYSTEM, "test", SYSTEM_UID);
 
         CountDownLatch latch = new CountDownLatch(1);
         final int[] actualStatus = new int[2];
@@ -4977,12 +4979,11 @@
         };
         mZenModeHelper.addCallback(callback);
 
-        mZenModeHelper.setAutomaticZenRuleState(createdId,
-                new Condition(zenRule.getConditionId(), "", STATE_TRUE),
-                ZenModeConfig.ORIGIN_SYSTEM, SYSTEM_UID);
+        mZenModeHelper.setAutomaticZenRuleState(UserHandle.CURRENT, createdId,
+                new Condition(zenRule.getConditionId(), "", STATE_TRUE), ORIGIN_SYSTEM, SYSTEM_UID);
 
-        mZenModeHelper.setManualZenMode(Global.ZEN_MODE_OFF, null, ZenModeConfig.ORIGIN_SYSTEM,
-                null, "", SYSTEM_UID);
+        mZenModeHelper.setManualZenMode(UserHandle.CURRENT, Global.ZEN_MODE_OFF, null,
+                ORIGIN_SYSTEM, null, "", SYSTEM_UID);
 
         assertTrue(latch.await(500, TimeUnit.MILLISECONDS));
         if (CompatChanges.isChangeEnabled(ZenModeHelper.SEND_ACTIVATION_AZR_STATUSES)) {
@@ -5004,8 +5005,8 @@
                 ZenModeConfig.toScheduleConditionId(new ScheduleInfo()),
                 null,
                 NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
-        final String createdId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(),
-                zenRule, ZenModeConfig.ORIGIN_SYSTEM, "test", SYSTEM_UID);
+        final String createdId = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT,
+                mContext.getPackageName(), zenRule, ORIGIN_SYSTEM, "test", SYSTEM_UID);
 
         CountDownLatch latch = new CountDownLatch(1);
         final int[] actualStatus = new int[2];
@@ -5022,13 +5023,12 @@
         };
         mZenModeHelper.addCallback(callback);
 
-        mZenModeHelper.setAutomaticZenRuleState(createdId,
-                new Condition(zenRule.getConditionId(), "", STATE_TRUE),
-                ZenModeConfig.ORIGIN_SYSTEM, SYSTEM_UID);
+        mZenModeHelper.setAutomaticZenRuleState(UserHandle.CURRENT, createdId,
+                new Condition(zenRule.getConditionId(), "", STATE_TRUE), ORIGIN_SYSTEM, SYSTEM_UID);
 
-        mZenModeHelper.setAutomaticZenRuleState(createdId,
-                new Condition(zenRule.getConditionId(), "", STATE_FALSE),
-                ORIGIN_APP, CUSTOM_PKG_UID);
+        mZenModeHelper.setAutomaticZenRuleState(UserHandle.CURRENT, createdId,
+                new Condition(zenRule.getConditionId(), "", STATE_FALSE), ORIGIN_APP,
+                CUSTOM_PKG_UID);
 
         assertTrue(latch.await(500, TimeUnit.MILLISECONDS));
         if (CompatChanges.isChangeEnabled(ZenModeHelper.SEND_ACTIVATION_AZR_STATUSES)) {
@@ -5049,21 +5049,20 @@
                 ZenModeConfig.toScheduleConditionId(new ScheduleInfo()),
                 null,
                 NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
-        final String createdId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(),
-                zenRule, ZenModeConfig.ORIGIN_SYSTEM, "test", SYSTEM_UID);
+        final String createdId = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT,
+                mContext.getPackageName(), zenRule, ORIGIN_SYSTEM, "test", SYSTEM_UID);
 
         // Event 1: Mimic the rule coming on automatically by setting the Condition to STATE_TRUE
-        mZenModeHelper.setAutomaticZenRuleState(createdId,
-                new Condition(zenRule.getConditionId(), "", STATE_TRUE),
-                ZenModeConfig.ORIGIN_SYSTEM, SYSTEM_UID);
+        mZenModeHelper.setAutomaticZenRuleState(UserHandle.CURRENT, createdId,
+                new Condition(zenRule.getConditionId(), "", STATE_TRUE), ORIGIN_SYSTEM, SYSTEM_UID);
 
         // Event 2: Snooze rule by turning off DND
-        mZenModeHelper.setManualZenMode(Global.ZEN_MODE_OFF, null, ZenModeConfig.ORIGIN_SYSTEM,
-                "", null, SYSTEM_UID);
+        mZenModeHelper.setManualZenMode(UserHandle.CURRENT, Global.ZEN_MODE_OFF, null,
+                ORIGIN_SYSTEM, "", null, SYSTEM_UID);
 
         // Event 3: "User" turns off the automatic rule (sets it to not enabled)
         zenRule.setEnabled(false);
-        mZenModeHelper.updateAutomaticZenRule(createdId, zenRule, ZenModeConfig.ORIGIN_SYSTEM,
+        mZenModeHelper.updateAutomaticZenRule(UserHandle.CURRENT, createdId, zenRule, ORIGIN_SYSTEM,
                 "", SYSTEM_UID);
 
         assertEquals(OVERRIDE_NONE,
@@ -5078,17 +5077,17 @@
                 .setConfigurationActivity(new ComponentName(mPkg, "cls"))
                 .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
                 .build();
-        String ruleId = mZenModeHelper.addAutomaticZenRule(mPkg, rule, ORIGIN_APP, "reason",
-                CUSTOM_PKG_UID);
-        mZenModeHelper.setAutomaticZenRuleState(ruleId, CONDITION_TRUE, ORIGIN_APP,
-                CUSTOM_PKG_UID);
+        String ruleId = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT, mPkg, rule,
+                ORIGIN_APP, "reason", CUSTOM_PKG_UID);
+        mZenModeHelper.setAutomaticZenRuleState(UserHandle.CURRENT, ruleId, CONDITION_TRUE,
+                ORIGIN_APP, CUSTOM_PKG_UID);
         assertThat(mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_IMPORTANT_INTERRUPTIONS);
 
         AutomaticZenRule updateWithDiff = new AutomaticZenRule.Builder(rule)
                 .setTriggerDescription("Whenever")
                 .build();
-        mZenModeHelper.updateAutomaticZenRule(ruleId, updateWithDiff, ORIGIN_APP, "reason",
-                CUSTOM_PKG_UID);
+        mZenModeHelper.updateAutomaticZenRule(UserHandle.CURRENT, ruleId, updateWithDiff,
+                ORIGIN_APP, "reason", CUSTOM_PKG_UID);
 
         assertThat(mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_OFF);
         assertThat(mZenModeHelper.mConfig.automaticRules.get(ruleId).condition).isNull();
@@ -5102,15 +5101,15 @@
                 .setConfigurationActivity(new ComponentName(mPkg, "cls"))
                 .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
                 .build();
-        String ruleId = mZenModeHelper.addAutomaticZenRule(mPkg, rule, ORIGIN_APP, "reason",
-                CUSTOM_PKG_UID);
-        mZenModeHelper.setAutomaticZenRuleState(ruleId, CONDITION_TRUE, ORIGIN_APP,
-                CUSTOM_PKG_UID);
+        String ruleId = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT, mPkg, rule,
+                ORIGIN_APP, "reason", CUSTOM_PKG_UID);
+        mZenModeHelper.setAutomaticZenRuleState(UserHandle.CURRENT, ruleId, CONDITION_TRUE,
+                ORIGIN_APP, CUSTOM_PKG_UID);
         assertThat(mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_IMPORTANT_INTERRUPTIONS);
 
         AutomaticZenRule updateUnchanged = new AutomaticZenRule.Builder(rule).build();
-        mZenModeHelper.updateAutomaticZenRule(ruleId, updateUnchanged, ORIGIN_APP, "reason",
-                CUSTOM_PKG_UID);
+        mZenModeHelper.updateAutomaticZenRule(UserHandle.CURRENT, ruleId, updateUnchanged,
+                ORIGIN_APP, "reason", CUSTOM_PKG_UID);
 
         assertThat(mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_IMPORTANT_INTERRUPTIONS);
         assertThat(mZenModeHelper.mConfig.automaticRules.get(ruleId).condition).isEqualTo(
@@ -5125,17 +5124,17 @@
                 .setConfigurationActivity(new ComponentName(mPkg, "cls"))
                 .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
                 .build();
-        String ruleId = mZenModeHelper.addAutomaticZenRule(mPkg, rule, ORIGIN_APP, "reason",
-                CUSTOM_PKG_UID);
-        mZenModeHelper.setAutomaticZenRuleState(ruleId, CONDITION_TRUE, ORIGIN_APP,
-                CUSTOM_PKG_UID);
+        String ruleId = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT, mPkg, rule,
+                ORIGIN_APP, "reason", CUSTOM_PKG_UID);
+        mZenModeHelper.setAutomaticZenRuleState(UserHandle.CURRENT, ruleId, CONDITION_TRUE,
+                ORIGIN_APP, CUSTOM_PKG_UID);
         assertThat(mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_IMPORTANT_INTERRUPTIONS);
 
         AutomaticZenRule updateWithDiff = new AutomaticZenRule.Builder(rule)
                 .setTriggerDescription("Whenever")
                 .build();
-        mZenModeHelper.updateAutomaticZenRule(ruleId, updateWithDiff, ORIGIN_USER_IN_SYSTEMUI,
-                "reason", SYSTEM_UID);
+        mZenModeHelper.updateAutomaticZenRule(UserHandle.CURRENT, ruleId, updateWithDiff,
+                ORIGIN_USER_IN_SYSTEMUI, "reason", SYSTEM_UID);
 
         assertThat(mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_IMPORTANT_INTERRUPTIONS);
         assertThat(mZenModeHelper.mConfig.automaticRules.get(ruleId).condition).isEqualTo(
@@ -5153,17 +5152,16 @@
                         .setConfigurationActivity(new ComponentName(mPkg, "cls"))
                         .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
                         .build();
-        String ruleId =
-                mZenModeHelper.addAutomaticZenRule(
-                        mPkg, rule, ORIGIN_APP, "reason", CUSTOM_PKG_UID);
-        mZenModeHelper.setAutomaticZenRuleState(
-                ruleId, CONDITION_TRUE, ORIGIN_APP, CUSTOM_PKG_UID);
+        String ruleId = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT, mPkg, rule,
+                ORIGIN_APP, "reason", CUSTOM_PKG_UID);
+        mZenModeHelper.setAutomaticZenRuleState(UserHandle.CURRENT, ruleId, CONDITION_TRUE,
+                ORIGIN_APP, CUSTOM_PKG_UID);
         assertThat(mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_IMPORTANT_INTERRUPTIONS);
 
         AutomaticZenRule updateWithDiff =
                 new AutomaticZenRule.Builder(rule).setTriggerDescription("Whenever").build();
-        mZenModeHelper.updateAutomaticZenRule(ruleId, updateWithDiff, ORIGIN_USER_IN_SYSTEMUI,
-                "reason", SYSTEM_UID);
+        mZenModeHelper.updateAutomaticZenRule(UserHandle.CURRENT, ruleId, updateWithDiff,
+                ORIGIN_USER_IN_SYSTEMUI, "reason", SYSTEM_UID);
 
         assertThat(mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_IMPORTANT_INTERRUPTIONS);
         assertThat(mZenModeHelper.mConfig.automaticRules.get(ruleId).condition).isEqualTo(
@@ -5178,16 +5176,16 @@
                 .setConfigurationActivity(new ComponentName(mPkg, "cls"))
                 .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
                 .build();
-        String ruleId = mZenModeHelper.addAutomaticZenRule(mPkg, rule, ORIGIN_APP, "reason",
-                CUSTOM_PKG_UID);
-        mZenModeHelper.setAutomaticZenRuleState(ruleId, CONDITION_TRUE, ORIGIN_APP,
-                CUSTOM_PKG_UID);
+        String ruleId = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT, mPkg, rule,
+                ORIGIN_APP, "reason", CUSTOM_PKG_UID);
+        mZenModeHelper.setAutomaticZenRuleState(UserHandle.CURRENT, ruleId, CONDITION_TRUE,
+                ORIGIN_APP, CUSTOM_PKG_UID);
         assertThat(mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_IMPORTANT_INTERRUPTIONS);
 
-        mZenModeHelper.updateAutomaticZenRule(ruleId,
+        mZenModeHelper.updateAutomaticZenRule(UserHandle.CURRENT, ruleId,
                 new AutomaticZenRule.Builder(rule).setEnabled(false).build(),
                 ORIGIN_USER_IN_SYSTEMUI, "disable", SYSTEM_UID);
-        mZenModeHelper.updateAutomaticZenRule(ruleId,
+        mZenModeHelper.updateAutomaticZenRule(UserHandle.CURRENT, ruleId,
                 new AutomaticZenRule.Builder(rule).setEnabled(true).build(),
                 ORIGIN_USER_IN_SYSTEMUI, "enable", SYSTEM_UID);
 
@@ -5203,16 +5201,16 @@
         AutomaticZenRule original = new AutomaticZenRule.Builder("Rule", Uri.EMPTY)
                 .setOwner(new ComponentName("android", "some.old.cps"))
                 .build();
-        String ruleId = mZenModeHelper.addAutomaticZenRule("android", original,
-                ZenModeConfig.ORIGIN_SYSTEM, "reason", SYSTEM_UID);
+        String ruleId = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT, "android", original,
+                ORIGIN_SYSTEM, "reason", SYSTEM_UID);
 
         AutomaticZenRule update = new AutomaticZenRule.Builder("Rule", Uri.EMPTY)
                 .setOwner(new ComponentName("android", "brand.new.cps"))
                 .build();
-        mZenModeHelper.updateAutomaticZenRule(ruleId, update, ORIGIN_USER_IN_SYSTEMUI, "reason",
-                SYSTEM_UID);
+        mZenModeHelper.updateAutomaticZenRule(UserHandle.CURRENT, ruleId, update,
+                ORIGIN_USER_IN_SYSTEMUI, "reason", SYSTEM_UID);
 
-        AutomaticZenRule result = mZenModeHelper.getAutomaticZenRule(ruleId);
+        AutomaticZenRule result = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId);
         assertThat(result).isNotNull();
         assertThat(result.getOwner().getClassName()).isEqualTo("brand.new.cps");
     }
@@ -5223,16 +5221,16 @@
         AutomaticZenRule original = new AutomaticZenRule.Builder("Rule", Uri.EMPTY)
                 .setOwner(new ComponentName(mContext.getPackageName(), "old.third.party.cps"))
                 .build();
-        String ruleId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), original,
-                ORIGIN_APP, "reason", CUSTOM_PKG_UID);
+        String ruleId = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT,
+                mContext.getPackageName(), original, ORIGIN_APP, "reason", CUSTOM_PKG_UID);
 
         AutomaticZenRule update = new AutomaticZenRule.Builder("Rule", Uri.EMPTY)
                 .setOwner(new ComponentName(mContext.getPackageName(), "new.third.party.cps"))
                 .build();
-        mZenModeHelper.updateAutomaticZenRule(ruleId, update, ORIGIN_USER_IN_SYSTEMUI, "reason",
-                SYSTEM_UID);
+        mZenModeHelper.updateAutomaticZenRule(UserHandle.CURRENT, ruleId, update,
+                ORIGIN_USER_IN_SYSTEMUI, "reason", SYSTEM_UID);
 
-        AutomaticZenRule result = mZenModeHelper.getAutomaticZenRule(ruleId);
+        AutomaticZenRule result = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId);
         assertThat(result).isNotNull();
         assertThat(result.getOwner().getClassName()).isEqualTo("old.third.party.cps");
     }
@@ -5247,14 +5245,14 @@
                 .setShouldSuppressAmbientDisplay(true)
                 .setShouldDimWallpaper(true)
                 .build());
-        mZenModeHelper.setAutomaticZenRuleState(ruleId, CONDITION_TRUE, ORIGIN_APP,
-                CUSTOM_PKG_UID);
+        mZenModeHelper.setAutomaticZenRuleState(UserHandle.CURRENT, ruleId, CONDITION_TRUE,
+                ORIGIN_APP, CUSTOM_PKG_UID);
         mTestableLooper.processAllMessages();
         verify(mDeviceEffectsApplier).apply(any(), eq(ORIGIN_APP));
 
         // Now delete the (currently active!) rule. For example, assume this is done from settings.
-        mZenModeHelper.removeAutomaticZenRule(ruleId, ORIGIN_USER_IN_SYSTEMUI, "remove",
-                SYSTEM_UID);
+        mZenModeHelper.removeAutomaticZenRule(UserHandle.CURRENT, ruleId, ORIGIN_USER_IN_SYSTEMUI,
+                "remove", SYSTEM_UID);
         mTestableLooper.processAllMessages();
 
         verify(mDeviceEffectsApplier).apply(eq(NO_EFFECTS), eq(ORIGIN_USER_IN_SYSTEMUI));
@@ -5273,8 +5271,8 @@
         String ruleId = addRuleWithEffects(effects);
         verifyNoMoreInteractions(mDeviceEffectsApplier);
 
-        mZenModeHelper.setAutomaticZenRuleState(ruleId, CONDITION_TRUE, ORIGIN_APP,
-                CUSTOM_PKG_UID);
+        mZenModeHelper.setAutomaticZenRuleState(UserHandle.CURRENT, ruleId, CONDITION_TRUE,
+                ORIGIN_APP, CUSTOM_PKG_UID);
         mTestableLooper.processAllMessages();
 
         verify(mDeviceEffectsApplier).apply(eq(effects), eq(ORIGIN_APP));
@@ -5287,13 +5285,13 @@
 
         ZenDeviceEffects zde = new ZenDeviceEffects.Builder().setShouldUseNightMode(true).build();
         String ruleId = addRuleWithEffects(zde);
-        mZenModeHelper.setAutomaticZenRuleState(ruleId, CONDITION_TRUE, ORIGIN_APP,
-                CUSTOM_PKG_UID);
+        mZenModeHelper.setAutomaticZenRuleState(UserHandle.CURRENT, ruleId, CONDITION_TRUE,
+                ORIGIN_APP, CUSTOM_PKG_UID);
         mTestableLooper.processAllMessages();
         verify(mDeviceEffectsApplier).apply(eq(zde), eq(ORIGIN_APP));
 
-        mZenModeHelper.setAutomaticZenRuleState(ruleId, CONDITION_FALSE, ORIGIN_APP,
-                CUSTOM_PKG_UID);
+        mZenModeHelper.setAutomaticZenRuleState(UserHandle.CURRENT, ruleId, CONDITION_FALSE,
+                ORIGIN_APP, CUSTOM_PKG_UID);
         mTestableLooper.processAllMessages();
 
         verify(mDeviceEffectsApplier).apply(eq(NO_EFFECTS), eq(ORIGIN_APP));
@@ -5310,8 +5308,8 @@
                         .setShouldDisplayGrayscale(true)
                         .addExtraEffect("ONE")
                         .build());
-        mZenModeHelper.setAutomaticZenRuleState(ruleId, CONDITION_TRUE, ORIGIN_APP,
-                CUSTOM_PKG_UID);
+        mZenModeHelper.setAutomaticZenRuleState(UserHandle.CURRENT, ruleId, CONDITION_TRUE,
+                ORIGIN_APP, CUSTOM_PKG_UID);
         mTestableLooper.processAllMessages();
         verify(mDeviceEffectsApplier).apply(
                 eq(new ZenDeviceEffects.Builder()
@@ -5326,8 +5324,8 @@
                         .setShouldDimWallpaper(true)
                         .addExtraEffect("TWO")
                         .build());
-        mZenModeHelper.setAutomaticZenRuleState(secondRuleId, CONDITION_TRUE, ORIGIN_APP,
-                CUSTOM_PKG_UID);
+        mZenModeHelper.setAutomaticZenRuleState(UserHandle.CURRENT, secondRuleId, CONDITION_TRUE,
+                ORIGIN_APP, CUSTOM_PKG_UID);
         mTestableLooper.processAllMessages();
 
         verify(mDeviceEffectsApplier).apply(
@@ -5350,15 +5348,15 @@
                 .addExtraEffect("extra_effect")
                 .build();
         String ruleId = addRuleWithEffects(zde);
-        mZenModeHelper.setAutomaticZenRuleState(ruleId, CONDITION_TRUE, ORIGIN_APP,
-                CUSTOM_PKG_UID);
+        mZenModeHelper.setAutomaticZenRuleState(UserHandle.CURRENT, ruleId, CONDITION_TRUE,
+                ORIGIN_APP, CUSTOM_PKG_UID);
         mTestableLooper.processAllMessages();
         verify(mDeviceEffectsApplier).apply(eq(zde), eq(ORIGIN_APP));
 
         // Now create and activate a second rule that doesn't add any more effects.
         String secondRuleId = addRuleWithEffects(zde);
-        mZenModeHelper.setAutomaticZenRuleState(secondRuleId, CONDITION_TRUE, ORIGIN_APP,
-                CUSTOM_PKG_UID);
+        mZenModeHelper.setAutomaticZenRuleState(UserHandle.CURRENT, secondRuleId, CONDITION_TRUE,
+                ORIGIN_APP, CUSTOM_PKG_UID);
         mTestableLooper.processAllMessages();
 
         verifyNoMoreInteractions(mDeviceEffectsApplier);
@@ -5371,8 +5369,8 @@
         String ruleId = addRuleWithEffects(zde);
         verify(mDeviceEffectsApplier, never()).apply(any(), anyInt());
 
-        mZenModeHelper.setAutomaticZenRuleState(ruleId, CONDITION_TRUE, ORIGIN_APP,
-                CUSTOM_PKG_UID);
+        mZenModeHelper.setAutomaticZenRuleState(UserHandle.CURRENT, ruleId, CONDITION_TRUE,
+                ORIGIN_APP, CUSTOM_PKG_UID);
         mTestableLooper.processAllMessages();
         verify(mDeviceEffectsApplier, never()).apply(any(), anyInt());
 
@@ -5412,8 +5410,8 @@
         AutomaticZenRule rule = new AutomaticZenRule.Builder("Test", CONDITION_ID)
                 .setDeviceEffects(effects)
                 .build();
-        return mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), rule,
-                ZenModeConfig.ORIGIN_SYSTEM, "reasons", SYSTEM_UID);
+        return mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT, mContext.getPackageName(),
+                rule, ORIGIN_SYSTEM, "reasons", SYSTEM_UID);
     }
 
     @Test
@@ -5426,35 +5424,37 @@
                 .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
                 .setZenPolicy(new ZenPolicy.Builder().allowRepeatCallers(false).build())
                 .build();
-        String ruleId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), rule,
-                ORIGIN_APP, "add it", CUSTOM_PKG_UID);
-        assertThat(mZenModeHelper.getAutomaticZenRule(ruleId).getCreationTime()).isEqualTo(1000);
+        String ruleId = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT,
+                mContext.getPackageName(), rule, ORIGIN_APP, "add it", CUSTOM_PKG_UID);
+        assertThat(mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId).getCreationTime())
+                .isEqualTo(1000);
 
         // User customizes it.
         AutomaticZenRule userUpdate = new AutomaticZenRule.Builder(rule)
                 .setInterruptionFilter(INTERRUPTION_FILTER_ALARMS)
                 .setZenPolicy(new ZenPolicy.Builder().allowRepeatCallers(true).build())
                 .build();
-        mZenModeHelper.updateAutomaticZenRule(ruleId, userUpdate, ORIGIN_USER_IN_SYSTEMUI,
-                "userUpdate", SYSTEM_UID);
+        mZenModeHelper.updateAutomaticZenRule(UserHandle.CURRENT, ruleId, userUpdate,
+                ORIGIN_USER_IN_SYSTEMUI, "userUpdate", SYSTEM_UID);
 
         // App deletes it.
         mTestClock.advanceByMillis(1000);
-        mZenModeHelper.removeAutomaticZenRule(ruleId, ORIGIN_APP, "delete it",
+        mZenModeHelper.removeAutomaticZenRule(UserHandle.CURRENT, ruleId, ORIGIN_APP, "delete it",
                 CUSTOM_PKG_UID);
         assertThat(mZenModeHelper.mConfig.automaticRules).hasSize(0);
         assertThat(mZenModeHelper.mConfig.deletedRules).hasSize(1);
 
         // App adds it again.
         mTestClock.advanceByMillis(1000);
-        String newRuleId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), rule,
-                ORIGIN_APP, "add it again", CUSTOM_PKG_UID);
+        String newRuleId = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT,
+                mContext.getPackageName(), rule, ORIGIN_APP, "add it again", CUSTOM_PKG_UID);
 
         // Verify that the rule was restored:
         // - id and creation time is the same as the original one.
         // - ZenPolicy is the one that the user had set.
         // - rule still has the user-modified fields.
-        AutomaticZenRule finalRule = mZenModeHelper.getAutomaticZenRule(newRuleId);
+        AutomaticZenRule finalRule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT,
+                newRuleId);
         assertThat(finalRule.getCreationTime()).isEqualTo(1000); // And not 3000.
         assertThat(newRuleId).isEqualTo(ruleId);
         assertThat(finalRule.getInterruptionFilter()).isEqualTo(INTERRUPTION_FILTER_ALARMS);
@@ -5481,24 +5481,26 @@
                 .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
                 .setZenPolicy(new ZenPolicy.Builder().allowRepeatCallers(false).build())
                 .build();
-        String ruleId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), rule,
-                ORIGIN_APP, "add it", CUSTOM_PKG_UID);
-        assertThat(mZenModeHelper.getAutomaticZenRule(ruleId).getCreationTime()).isEqualTo(1000);
+        String ruleId = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT,
+                mContext.getPackageName(), rule, ORIGIN_APP, "add it", CUSTOM_PKG_UID);
+        assertThat(mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId).getCreationTime())
+                .isEqualTo(1000);
 
         // App deletes it.
         mTestClock.advanceByMillis(1000);
-        mZenModeHelper.removeAutomaticZenRule(ruleId, ORIGIN_APP, "delete it",
+        mZenModeHelper.removeAutomaticZenRule(UserHandle.CURRENT, ruleId, ORIGIN_APP, "delete it",
                 CUSTOM_PKG_UID);
         assertThat(mZenModeHelper.mConfig.automaticRules).hasSize(0);
         assertThat(mZenModeHelper.mConfig.deletedRules).hasSize(0);
 
         // App adds it again.
         mTestClock.advanceByMillis(1000);
-        String newRuleId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), rule,
-                ORIGIN_APP, "add it again", CUSTOM_PKG_UID);
+        String newRuleId = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT,
+                mContext.getPackageName(), rule, ORIGIN_APP, "add it again", CUSTOM_PKG_UID);
 
         // Verify that the rule was recreated. This means id and creation time are new.
-        AutomaticZenRule finalRule = mZenModeHelper.getAutomaticZenRule(newRuleId);
+        AutomaticZenRule finalRule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT,
+                newRuleId);
         assertThat(finalRule.getCreationTime()).isEqualTo(3000);
         assertThat(newRuleId).isNotEqualTo(ruleId);
     }
@@ -5513,9 +5515,10 @@
                 .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
                 .setZenPolicy(new ZenPolicy.Builder().allowRepeatCallers(false).build())
                 .build();
-        String ruleId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), rule,
-                ORIGIN_APP, "add it", CUSTOM_PKG_UID);
-        assertThat(mZenModeHelper.getAutomaticZenRule(ruleId).getCreationTime()).isEqualTo(1000);
+        String ruleId = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT,
+                mContext.getPackageName(), rule, ORIGIN_APP, "add it", CUSTOM_PKG_UID);
+        assertThat(mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId).getCreationTime())
+                .isEqualTo(1000);
 
         // User customizes it.
         mTestClock.advanceByMillis(1000);
@@ -5523,24 +5526,26 @@
                 .setInterruptionFilter(INTERRUPTION_FILTER_ALARMS)
                 .setZenPolicy(new ZenPolicy.Builder().allowRepeatCallers(true).build())
                 .build();
-        mZenModeHelper.updateAutomaticZenRule(ruleId, userUpdate, ORIGIN_USER_IN_SYSTEMUI,
-                "userUpdate", SYSTEM_UID);
+        mZenModeHelper.updateAutomaticZenRule(UserHandle.CURRENT, ruleId, userUpdate,
+                ORIGIN_USER_IN_SYSTEMUI, "userUpdate", SYSTEM_UID);
 
         // App deletes it.
         mTestClock.advanceByMillis(1000);
-        mZenModeHelper.removeAutomaticZenRule(ruleId, ORIGIN_APP, "delete it",
+        mZenModeHelper.removeAutomaticZenRule(UserHandle.CURRENT, ruleId, ORIGIN_APP, "delete it",
                 CUSTOM_PKG_UID);
         assertThat(mZenModeHelper.mConfig.automaticRules).hasSize(0);
         assertThat(mZenModeHelper.mConfig.deletedRules).hasSize(1);
 
         // User creates it again (unusual case, but ok).
         mTestClock.advanceByMillis(1000);
-        String newRuleId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), rule,
-                ORIGIN_USER_IN_SYSTEMUI, "add it anew", SYSTEM_UID);
+        String newRuleId = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT,
+                mContext.getPackageName(), rule, ORIGIN_USER_IN_SYSTEMUI, "add it anew",
+                SYSTEM_UID);
 
         // Verify that the rule was recreated. This means id and creation time are new, and the rule
         // matches the latest data supplied to addAZR.
-        AutomaticZenRule finalRule = mZenModeHelper.getAutomaticZenRule(newRuleId);
+        AutomaticZenRule finalRule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT,
+                newRuleId);
         assertThat(finalRule.getCreationTime()).isEqualTo(4000);
         assertThat(newRuleId).isNotEqualTo(ruleId);
         assertThat(finalRule.getInterruptionFilter()).isEqualTo(INTERRUPTION_FILTER_PRIORITY);
@@ -5561,9 +5566,10 @@
                 .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
                 .setZenPolicy(new ZenPolicy.Builder().allowRepeatCallers(false).build())
                 .build();
-        String ruleId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), rule,
-                ORIGIN_APP, "add it", CUSTOM_PKG_UID);
-        assertThat(mZenModeHelper.getAutomaticZenRule(ruleId).getCreationTime()).isEqualTo(1000);
+        String ruleId = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT,
+                mContext.getPackageName(), rule, ORIGIN_APP, "add it", CUSTOM_PKG_UID);
+        assertThat(mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId).getCreationTime())
+                .isEqualTo(1000);
 
         // User customizes it.
         mTestClock.advanceByMillis(1000);
@@ -5571,23 +5577,24 @@
                 .setInterruptionFilter(INTERRUPTION_FILTER_ALARMS)
                 .setZenPolicy(new ZenPolicy.Builder().allowRepeatCallers(true).build())
                 .build();
-        mZenModeHelper.updateAutomaticZenRule(ruleId, userUpdate, ORIGIN_USER_IN_SYSTEMUI,
-                "userUpdate", SYSTEM_UID);
+        mZenModeHelper.updateAutomaticZenRule(UserHandle.CURRENT, ruleId, userUpdate,
+                ORIGIN_USER_IN_SYSTEMUI, "userUpdate", SYSTEM_UID);
 
         // User deletes it.
         mTestClock.advanceByMillis(1000);
-        mZenModeHelper.removeAutomaticZenRule(ruleId, ORIGIN_USER_IN_SYSTEMUI, "delete it",
-                SYSTEM_UID);
+        mZenModeHelper.removeAutomaticZenRule(UserHandle.CURRENT, ruleId, ORIGIN_USER_IN_SYSTEMUI,
+                "delete it", SYSTEM_UID);
         assertThat(mZenModeHelper.mConfig.automaticRules).hasSize(0);
         assertThat(mZenModeHelper.mConfig.deletedRules).hasSize(0);
 
         // App creates it again.
         mTestClock.advanceByMillis(1000);
-        String newRuleId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), rule,
-                ORIGIN_APP, "add it again", CUSTOM_PKG_UID);
+        String newRuleId = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT,
+                mContext.getPackageName(), rule, ORIGIN_APP, "add it again", CUSTOM_PKG_UID);
 
         // Verify that the rule was recreated. This means id and creation time are new.
-        AutomaticZenRule finalRule = mZenModeHelper.getAutomaticZenRule(newRuleId);
+        AutomaticZenRule finalRule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT,
+                newRuleId);
         assertThat(finalRule.getCreationTime()).isEqualTo(4000);
         assertThat(newRuleId).isNotEqualTo(ruleId);
     }
@@ -5601,18 +5608,18 @@
                 .setOwner(new ComponentName("first", "owner"))
                 .setInterruptionFilter(INTERRUPTION_FILTER_ALL)
                 .build();
-        String ruleId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), rule,
-                ORIGIN_APP, "add it", CUSTOM_PKG_UID);
+        String ruleId = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT,
+                mContext.getPackageName(), rule, ORIGIN_APP, "add it", CUSTOM_PKG_UID);
 
         // User customizes it.
         AutomaticZenRule userUpdate = new AutomaticZenRule.Builder(rule)
                 .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
                 .build();
-        mZenModeHelper.updateAutomaticZenRule(ruleId, userUpdate, ORIGIN_USER_IN_SYSTEMUI,
-                "userUpdate", SYSTEM_UID);
+        mZenModeHelper.updateAutomaticZenRule(UserHandle.CURRENT, ruleId, userUpdate,
+                ORIGIN_USER_IN_SYSTEMUI, "userUpdate", SYSTEM_UID);
 
         // App deletes it. It's preserved for a possible restoration.
-        mZenModeHelper.removeAutomaticZenRule(ruleId, ORIGIN_APP, "delete it",
+        mZenModeHelper.removeAutomaticZenRule(UserHandle.CURRENT, ruleId, ORIGIN_APP, "delete it",
                 CUSTOM_PKG_UID);
         assertThat(mZenModeHelper.mConfig.automaticRules).hasSize(0);
         assertThat(mZenModeHelper.mConfig.deletedRules).hasSize(1);
@@ -5622,12 +5629,14 @@
                 .setOwner(new ComponentName("second", "owner"))
                 .setInterruptionFilter(INTERRUPTION_FILTER_ALARMS)
                 .build();
-        String newRuleId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(),
-                readdingWithDifferentOwner, ORIGIN_APP, "add it again", CUSTOM_PKG_UID);
+        String newRuleId = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT,
+                mContext.getPackageName(), readdingWithDifferentOwner, ORIGIN_APP, "add it again",
+                CUSTOM_PKG_UID);
 
         // Verify that the rule was NOT restored:
         assertThat(newRuleId).isNotEqualTo(ruleId);
-        AutomaticZenRule finalRule = mZenModeHelper.getAutomaticZenRule(newRuleId);
+        AutomaticZenRule finalRule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT,
+                newRuleId);
         assertThat(finalRule.getInterruptionFilter()).isEqualTo(INTERRUPTION_FILTER_ALARMS);
         assertThat(finalRule.getOwner()).isEqualTo(new ComponentName("second", "owner"));
 
@@ -5643,23 +5652,23 @@
         mZenModeHelper.mConfig.automaticRules.clear();
 
         // Start with a bunch of customized rules where conditionUris are not unique.
-        String id1 = mZenModeHelper.addAutomaticZenRule("pkg1",
+        String id1 = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT, "pkg1",
                 new AutomaticZenRule.Builder("Test1", Uri.parse("uri1")).build(),
                 ORIGIN_APP,
                 "add it", CUSTOM_PKG_UID);
-        String id2 = mZenModeHelper.addAutomaticZenRule("pkg1",
+        String id2 = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT, "pkg1",
                 new AutomaticZenRule.Builder("Test2", Uri.parse("uri2")).build(),
                 ORIGIN_APP,
                 "add it", CUSTOM_PKG_UID);
-        String id3 = mZenModeHelper.addAutomaticZenRule("pkg1",
+        String id3 = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT, "pkg1",
                 new AutomaticZenRule.Builder("Test3", Uri.parse("uri2")).build(),
                 ORIGIN_APP,
                 "add it", CUSTOM_PKG_UID);
-        String id4 = mZenModeHelper.addAutomaticZenRule("pkg2",
+        String id4 = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT, "pkg2",
                 new AutomaticZenRule.Builder("Test4", Uri.parse("uri1")).build(),
                 ORIGIN_APP,
                 "add it", CUSTOM_PKG_UID);
-        String id5 = mZenModeHelper.addAutomaticZenRule("pkg2",
+        String id5 = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT, "pkg2",
                 new AutomaticZenRule.Builder("Test5", Uri.parse("uri1")).build(),
                 ORIGIN_APP,
                 "add it", CUSTOM_PKG_UID);
@@ -5667,11 +5676,16 @@
             zenRule.userModifiedFields = AutomaticZenRule.FIELD_INTERRUPTION_FILTER;
         }
 
-        mZenModeHelper.removeAutomaticZenRule(id1, ORIGIN_APP, "begone", CUSTOM_PKG_UID);
-        mZenModeHelper.removeAutomaticZenRule(id2, ORIGIN_APP, "begone", CUSTOM_PKG_UID);
-        mZenModeHelper.removeAutomaticZenRule(id3, ORIGIN_APP, "begone", CUSTOM_PKG_UID);
-        mZenModeHelper.removeAutomaticZenRule(id4, ORIGIN_APP, "begone", CUSTOM_PKG_UID);
-        mZenModeHelper.removeAutomaticZenRule(id5, ORIGIN_APP, "begone", CUSTOM_PKG_UID);
+        mZenModeHelper.removeAutomaticZenRule(UserHandle.CURRENT, id1, ORIGIN_APP, "begone",
+                CUSTOM_PKG_UID);
+        mZenModeHelper.removeAutomaticZenRule(UserHandle.CURRENT, id2, ORIGIN_APP, "begone",
+                CUSTOM_PKG_UID);
+        mZenModeHelper.removeAutomaticZenRule(UserHandle.CURRENT, id3, ORIGIN_APP, "begone",
+                CUSTOM_PKG_UID);
+        mZenModeHelper.removeAutomaticZenRule(UserHandle.CURRENT, id4, ORIGIN_APP, "begone",
+                CUSTOM_PKG_UID);
+        mZenModeHelper.removeAutomaticZenRule(UserHandle.CURRENT, id5, ORIGIN_APP, "begone",
+                CUSTOM_PKG_UID);
 
         assertThat(mZenModeHelper.mConfig.deletedRules.keySet())
                 .containsExactly("pkg1|uri1", "pkg1|uri2", "pkg2|uri1");
@@ -5685,11 +5699,11 @@
     public void removeAllZenRules_preservedForRestoring() {
         mZenModeHelper.mConfig.automaticRules.clear();
 
-        mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(),
+        mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT, mContext.getPackageName(),
                 new AutomaticZenRule.Builder("Test1", Uri.parse("uri1")).build(),
                 ORIGIN_APP,
                 "add it", CUSTOM_PKG_UID);
-        mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(),
+        mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT, mContext.getPackageName(),
                 new AutomaticZenRule.Builder("Test2", Uri.parse("uri2")).build(),
                 ORIGIN_APP,
                 "add it", CUSTOM_PKG_UID);
@@ -5698,8 +5712,8 @@
             zenRule.userModifiedFields = AutomaticZenRule.FIELD_INTERRUPTION_FILTER;
         }
 
-        mZenModeHelper.removeAutomaticZenRules(mContext.getPackageName(), ORIGIN_APP,
-                "begone", CUSTOM_PKG_UID);
+        mZenModeHelper.removeAutomaticZenRules(UserHandle.CURRENT, mContext.getPackageName(),
+                ORIGIN_APP, "begone", CUSTOM_PKG_UID);
 
         assertThat(mZenModeHelper.mConfig.deletedRules).hasSize(2);
     }
@@ -5716,8 +5730,8 @@
         mZenModeHelper.mConfig.deletedRules.put(ZenModeConfig.deletedRuleKey(pkg1Rule), pkg1Rule);
         mZenModeHelper.mConfig.deletedRules.put(ZenModeConfig.deletedRuleKey(pkg2Rule), pkg2Rule);
 
-        mZenModeHelper.removeAutomaticZenRules("pkg1",
-                ZenModeConfig.ORIGIN_SYSTEM, "goodbye pkg1", SYSTEM_UID);
+        mZenModeHelper.removeAutomaticZenRules(UserHandle.CURRENT, "pkg1",
+                ORIGIN_SYSTEM, "goodbye pkg1", SYSTEM_UID);
 
         // Preserved rules from pkg1 are gone; those from pkg2 are still there.
         assertThat(mZenModeHelper.mConfig.deletedRules.values().stream().map(r -> r.pkg)
@@ -5733,36 +5747,37 @@
                 .setConditionId(CONDITION_ID)
                 .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
                 .build();
-        String ruleId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), rule,
-                ORIGIN_APP, "add it", CUSTOM_PKG_UID);
+        String ruleId = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT,
+                mContext.getPackageName(), rule, ORIGIN_APP, "add it", CUSTOM_PKG_UID);
 
         // User customizes it.
         AutomaticZenRule userUpdate = new AutomaticZenRule.Builder(rule)
                 .setInterruptionFilter(INTERRUPTION_FILTER_ALARMS)
                 .build();
-        mZenModeHelper.updateAutomaticZenRule(ruleId, userUpdate, ORIGIN_USER_IN_SYSTEMUI,
-                "userUpdate", SYSTEM_UID);
+        mZenModeHelper.updateAutomaticZenRule(UserHandle.CURRENT, ruleId, userUpdate,
+                ORIGIN_USER_IN_SYSTEMUI, "userUpdate", SYSTEM_UID);
         assertThat(mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_OFF);
 
         // App activates it.
-        mZenModeHelper.setAutomaticZenRuleState(ruleId, CONDITION_TRUE, ORIGIN_APP,
-                CUSTOM_PKG_UID);
+        mZenModeHelper.setAutomaticZenRuleState(UserHandle.CURRENT, ruleId, CONDITION_TRUE,
+                ORIGIN_APP, CUSTOM_PKG_UID);
         assertThat(mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_ALARMS);
 
         // App deletes it.
-        mZenModeHelper.removeAutomaticZenRule(ruleId, ORIGIN_APP, "delete it",
+        mZenModeHelper.removeAutomaticZenRule(UserHandle.CURRENT, ruleId, ORIGIN_APP, "delete it",
                 CUSTOM_PKG_UID);
         assertThat(mZenModeHelper.mConfig.automaticRules).hasSize(0);
         assertThat(mZenModeHelper.mConfig.deletedRules).hasSize(1);
         assertThat(mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_OFF);
 
         // App adds it again.
-        String newRuleId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), rule,
-                ORIGIN_APP, "add it again", CUSTOM_PKG_UID);
+        String newRuleId = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT,
+                mContext.getPackageName(), rule, ORIGIN_APP, "add it again", CUSTOM_PKG_UID);
 
         // The rule is restored...
         assertThat(newRuleId).isEqualTo(ruleId);
-        AutomaticZenRule finalRule = mZenModeHelper.getAutomaticZenRule(newRuleId);
+        AutomaticZenRule finalRule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT,
+                newRuleId);
         assertThat(finalRule.getInterruptionFilter()).isEqualTo(INTERRUPTION_FILTER_ALARMS);
 
         // ... but it is NOT active
@@ -5782,40 +5797,41 @@
                 .setConditionId(CONDITION_ID)
                 .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
                 .build();
-        String ruleId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), rule,
-                ORIGIN_APP, "add it", CUSTOM_PKG_UID);
+        String ruleId = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT,
+                mContext.getPackageName(), rule, ORIGIN_APP, "add it", CUSTOM_PKG_UID);
 
         // User customizes it.
         AutomaticZenRule userUpdate = new AutomaticZenRule.Builder(rule)
                 .setInterruptionFilter(INTERRUPTION_FILTER_ALARMS)
                 .build();
-        mZenModeHelper.updateAutomaticZenRule(ruleId, userUpdate, ORIGIN_USER_IN_SYSTEMUI,
-                "userUpdate", SYSTEM_UID);
+        mZenModeHelper.updateAutomaticZenRule(UserHandle.CURRENT, ruleId, userUpdate,
+                ORIGIN_USER_IN_SYSTEMUI, "userUpdate", SYSTEM_UID);
         assertThat(mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_OFF);
 
         // App activates it.
-        mZenModeHelper.setAutomaticZenRuleState(ruleId, CONDITION_TRUE, ORIGIN_APP,
-                CUSTOM_PKG_UID);
+        mZenModeHelper.setAutomaticZenRuleState(UserHandle.CURRENT, ruleId, CONDITION_TRUE,
+                ORIGIN_APP, CUSTOM_PKG_UID);
         assertThat(mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_ALARMS);
 
         // User snoozes it.
-        mZenModeHelper.setManualZenMode(ZEN_MODE_OFF, null, ZenModeConfig.ORIGIN_SYSTEM,
+        mZenModeHelper.setManualZenMode(UserHandle.CURRENT, ZEN_MODE_OFF, null, ORIGIN_SYSTEM,
                 "snoozing", "systemui", SYSTEM_UID);
         assertThat(mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_OFF);
 
         // App deletes it.
-        mZenModeHelper.removeAutomaticZenRule(ruleId, ORIGIN_APP, "delete it",
+        mZenModeHelper.removeAutomaticZenRule(UserHandle.CURRENT, ruleId, ORIGIN_APP, "delete it",
                 CUSTOM_PKG_UID);
         assertThat(mZenModeHelper.mConfig.automaticRules).hasSize(0);
         assertThat(mZenModeHelper.mConfig.deletedRules).hasSize(1);
 
         // App adds it again.
-        String newRuleId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), rule,
-                ORIGIN_APP, "add it again", CUSTOM_PKG_UID);
+        String newRuleId = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT,
+                mContext.getPackageName(), rule, ORIGIN_APP, "add it again", CUSTOM_PKG_UID);
 
         // The rule is restored...
         assertThat(newRuleId).isEqualTo(ruleId);
-        AutomaticZenRule finalRule = mZenModeHelper.getAutomaticZenRule(newRuleId);
+        AutomaticZenRule finalRule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT,
+                newRuleId);
         assertThat(finalRule.getInterruptionFilter()).isEqualTo(INTERRUPTION_FILTER_ALARMS);
 
         // ... but it is NEITHER active NOR snoozed.
@@ -5888,7 +5904,8 @@
     @Test
     @EnableFlags(FLAG_MODES_API)
     public void getAutomaticZenRuleState_ownedRule_returnsRuleState() {
-        String id = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(),
+        String id = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT,
+                mContext.getPackageName(),
                 new AutomaticZenRule.Builder("Rule", CONDITION_ID)
                         .setConfigurationActivity(
                                 new ComponentName(mContext.getPackageName(), "Blah"))
@@ -5896,18 +5913,23 @@
                 ORIGIN_APP, "reasons", CUSTOM_PKG_UID);
 
         // Null condition -> STATE_FALSE
-        assertThat(mZenModeHelper.getAutomaticZenRuleState(id)).isEqualTo(Condition.STATE_FALSE);
+        assertThat(mZenModeHelper.getAutomaticZenRuleState(UserHandle.CURRENT, id))
+                .isEqualTo(Condition.STATE_FALSE);
 
-        mZenModeHelper.setAutomaticZenRuleState(id, CONDITION_TRUE, ORIGIN_APP,
+        mZenModeHelper.setAutomaticZenRuleState(UserHandle.CURRENT, id, CONDITION_TRUE, ORIGIN_APP,
                 CUSTOM_PKG_UID);
-        assertThat(mZenModeHelper.getAutomaticZenRuleState(id)).isEqualTo(Condition.STATE_TRUE);
+        assertThat(mZenModeHelper.getAutomaticZenRuleState(UserHandle.CURRENT, id))
+                .isEqualTo(Condition.STATE_TRUE);
 
-        mZenModeHelper.setAutomaticZenRuleState(id, CONDITION_FALSE, ORIGIN_APP,
+        mZenModeHelper.setAutomaticZenRuleState(UserHandle.CURRENT, id, CONDITION_FALSE, ORIGIN_APP,
                 CUSTOM_PKG_UID);
-        assertThat(mZenModeHelper.getAutomaticZenRuleState(id)).isEqualTo(Condition.STATE_FALSE);
+        assertThat(mZenModeHelper.getAutomaticZenRuleState(UserHandle.CURRENT, id))
+                .isEqualTo(Condition.STATE_FALSE);
 
-        mZenModeHelper.removeAutomaticZenRule(id, ORIGIN_APP, "", CUSTOM_PKG_UID);
-        assertThat(mZenModeHelper.getAutomaticZenRuleState(id)).isEqualTo(Condition.STATE_UNKNOWN);
+        mZenModeHelper.removeAutomaticZenRule(UserHandle.CURRENT, id, ORIGIN_APP, "",
+                CUSTOM_PKG_UID);
+        assertThat(mZenModeHelper.getAutomaticZenRuleState(UserHandle.CURRENT, id))
+                .isEqualTo(Condition.STATE_UNKNOWN);
     }
 
     @Test
@@ -5922,8 +5944,8 @@
         mZenModeHelper.setConfig(config, null, ORIGIN_INIT, "", SYSTEM_UID);
         assertThat(mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_ALARMS);
 
-        assertThat(mZenModeHelper.getAutomaticZenRuleState("systemRule")).isEqualTo(
-                Condition.STATE_UNKNOWN);
+        assertThat(mZenModeHelper.getAutomaticZenRuleState(UserHandle.CURRENT, "systemRule"))
+                .isEqualTo(Condition.STATE_UNKNOWN);
     }
 
     @Test
@@ -5940,7 +5962,7 @@
         assertThat(mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_ALARMS);
 
         // Should be ignored.
-        mZenModeHelper.setAutomaticZenRuleState("otherRule",
+        mZenModeHelper.setAutomaticZenRuleState(UserHandle.CURRENT, "otherRule",
                 new Condition(otherRule.conditionId, "off", Condition.STATE_FALSE),
                 ORIGIN_APP, CUSTOM_PKG_UID);
 
@@ -5961,7 +5983,7 @@
         assertThat(mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_ALARMS);
 
         // Should be ignored.
-        mZenModeHelper.setAutomaticZenRuleState(otherRule.conditionId,
+        mZenModeHelper.setAutomaticZenRuleState(UserHandle.CURRENT, otherRule.conditionId,
                 new Condition(otherRule.conditionId, "off", Condition.STATE_FALSE),
                 ORIGIN_APP, CUSTOM_PKG_UID);
 
@@ -5972,7 +5994,8 @@
     @EnableFlags(FLAG_MODES_API)
     public void testCallbacks_policy() throws Exception {
         setupZenConfig();
-        assertThat(mZenModeHelper.getNotificationPolicy().allowReminders()).isTrue();
+        assertThat(mZenModeHelper.getNotificationPolicy(UserHandle.CURRENT).allowReminders())
+                .isTrue();
         SettableFuture<Policy> futurePolicy = SettableFuture.create();
         mZenModeHelper.addCallback(new ZenModeHelper.Callback() {
             @Override
@@ -5982,7 +6005,8 @@
         });
 
         Policy totalSilencePolicy = new Policy(0, 0, 0);
-        mZenModeHelper.setNotificationPolicy(totalSilencePolicy, ORIGIN_APP, CUSTOM_PKG_UID);
+        mZenModeHelper.setNotificationPolicy(UserHandle.CURRENT, totalSilencePolicy, ORIGIN_APP,
+                CUSTOM_PKG_UID);
 
         Policy callbackPolicy = futurePolicy.get(1, TimeUnit.SECONDS);
         assertThat(callbackPolicy.allowReminders()).isFalse();
@@ -6000,13 +6024,14 @@
             }
         });
 
-        String totalSilenceRuleId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(),
+        String totalSilenceRuleId = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT,
+                mContext.getPackageName(),
                 new AutomaticZenRule.Builder("Rule", CONDITION_ID)
                         .setOwner(OWNER)
                         .setInterruptionFilter(INTERRUPTION_FILTER_NONE)
                         .build(),
                 ORIGIN_APP, "reasons", 0);
-        mZenModeHelper.setAutomaticZenRuleState(totalSilenceRuleId,
+        mZenModeHelper.setAutomaticZenRuleState(UserHandle.CURRENT, totalSilenceRuleId,
                 new Condition(CONDITION_ID, "", STATE_TRUE), ORIGIN_APP, CUSTOM_PKG_UID);
 
         Policy callbackPolicy = futureConsolidatedPolicy.get(1, TimeUnit.SECONDS);
@@ -6018,8 +6043,8 @@
     public void applyGlobalZenModeAsImplicitZenRule_createsImplicitRuleAndActivatesIt() {
         mZenModeHelper.mConfig.automaticRules.clear();
 
-        mZenModeHelper.applyGlobalZenModeAsImplicitZenRule(CUSTOM_PKG_NAME, CUSTOM_PKG_UID,
-                ZEN_MODE_IMPORTANT_INTERRUPTIONS);
+        mZenModeHelper.applyGlobalZenModeAsImplicitZenRule(UserHandle.CURRENT, CUSTOM_PKG_NAME,
+                CUSTOM_PKG_UID, ZEN_MODE_IMPORTANT_INTERRUPTIONS);
 
         assertThat(mZenModeHelper.mConfig.automaticRules.values())
                 .comparingElementsUsing(IGNORE_METADATA)
@@ -6034,13 +6059,14 @@
     public void applyGlobalZenModeAsImplicitZenRule_updatesImplicitRuleAndActivatesIt() {
         mZenModeHelper.mConfig.automaticRules.clear();
 
-        mZenModeHelper.applyGlobalZenModeAsImplicitZenRule(CUSTOM_PKG_NAME, CUSTOM_PKG_UID,
-                ZEN_MODE_IMPORTANT_INTERRUPTIONS);
-        mZenModeHelper.setManualZenMode(ZEN_MODE_OFF, null, ORIGIN_APP, "test", "test", 0);
+        mZenModeHelper.applyGlobalZenModeAsImplicitZenRule(UserHandle.CURRENT, CUSTOM_PKG_NAME,
+                CUSTOM_PKG_UID, ZEN_MODE_IMPORTANT_INTERRUPTIONS);
+        mZenModeHelper.setManualZenMode(UserHandle.CURRENT, ZEN_MODE_OFF, null, ORIGIN_APP, "test",
+                "test", 0);
         assertThat(mZenModeHelper.mConfig.automaticRules).hasSize(1);
 
-        mZenModeHelper.applyGlobalZenModeAsImplicitZenRule(CUSTOM_PKG_NAME, CUSTOM_PKG_UID,
-                ZEN_MODE_ALARMS);
+        mZenModeHelper.applyGlobalZenModeAsImplicitZenRule(UserHandle.CURRENT, CUSTOM_PKG_NAME,
+                CUSTOM_PKG_UID, ZEN_MODE_ALARMS);
 
         assertThat(mZenModeHelper.mConfig.automaticRules.values())
                 .comparingElementsUsing(IGNORE_METADATA)
@@ -6057,22 +6083,22 @@
         String pkg = mContext.getPackageName();
 
         // From app, call "setInterruptionFilter" and create and implicit rule.
-        mZenModeHelper.applyGlobalZenModeAsImplicitZenRule(pkg, CUSTOM_PKG_UID,
+        mZenModeHelper.applyGlobalZenModeAsImplicitZenRule(UserHandle.CURRENT, pkg, CUSTOM_PKG_UID,
                 ZEN_MODE_IMPORTANT_INTERRUPTIONS);
         String ruleId = getOnlyElement(mZenModeHelper.mConfig.automaticRules.keySet());
         assertThat(getOnlyElement(mZenModeHelper.mConfig.automaticRules.values()).zenMode)
                 .isEqualTo(ZEN_MODE_IMPORTANT_INTERRUPTIONS);
 
         // From user, update that rule's interruption filter.
-        AutomaticZenRule rule = mZenModeHelper.getAutomaticZenRule(ruleId);
+        AutomaticZenRule rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId);
         AutomaticZenRule userUpdateRule = new AutomaticZenRule.Builder(rule)
                 .setInterruptionFilter(INTERRUPTION_FILTER_ALARMS)
                 .build();
-        mZenModeHelper.updateAutomaticZenRule(ruleId, userUpdateRule, ORIGIN_USER_IN_SYSTEMUI,
-                "reason", SYSTEM_UID);
+        mZenModeHelper.updateAutomaticZenRule(UserHandle.CURRENT, ruleId, userUpdateRule,
+                ORIGIN_USER_IN_SYSTEMUI, "reason", SYSTEM_UID);
 
         // From app, call "setInterruptionFilter" again.
-        mZenModeHelper.applyGlobalZenModeAsImplicitZenRule(pkg, CUSTOM_PKG_UID,
+        mZenModeHelper.applyGlobalZenModeAsImplicitZenRule(UserHandle.CURRENT, pkg, CUSTOM_PKG_UID,
                 ZEN_MODE_NO_INTERRUPTIONS);
 
         // The app's update was ignored, and the user's update is still current, and the current
@@ -6089,22 +6115,22 @@
         String pkg = mContext.getPackageName();
 
         // From app, call "setInterruptionFilter" and create and implicit rule.
-        mZenModeHelper.applyGlobalZenModeAsImplicitZenRule(pkg, CUSTOM_PKG_UID,
+        mZenModeHelper.applyGlobalZenModeAsImplicitZenRule(UserHandle.CURRENT, pkg, CUSTOM_PKG_UID,
                 ZEN_MODE_IMPORTANT_INTERRUPTIONS);
         String ruleId = getOnlyElement(mZenModeHelper.mConfig.automaticRules.keySet());
         assertThat(getOnlyElement(mZenModeHelper.mConfig.automaticRules.values()).zenMode)
                 .isEqualTo(ZEN_MODE_IMPORTANT_INTERRUPTIONS);
 
         // From user, update something in that rule, but not the interruption filter.
-        AutomaticZenRule rule = mZenModeHelper.getAutomaticZenRule(ruleId);
+        AutomaticZenRule rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId);
         AutomaticZenRule userUpdateRule = new AutomaticZenRule.Builder(rule)
                 .setName("Renamed")
                 .build();
-        mZenModeHelper.updateAutomaticZenRule(ruleId, userUpdateRule, ORIGIN_USER_IN_SYSTEMUI,
-                "reason", SYSTEM_UID);
+        mZenModeHelper.updateAutomaticZenRule(UserHandle.CURRENT, ruleId, userUpdateRule,
+                ORIGIN_USER_IN_SYSTEMUI, "reason", SYSTEM_UID);
 
         // From app, call "setInterruptionFilter" again.
-        mZenModeHelper.applyGlobalZenModeAsImplicitZenRule(pkg, CUSTOM_PKG_UID,
+        mZenModeHelper.applyGlobalZenModeAsImplicitZenRule(UserHandle.CURRENT, pkg, CUSTOM_PKG_UID,
                 ZEN_MODE_NO_INTERRUPTIONS);
 
         // The app's update was accepted, and the current mode is the one that they wanted.
@@ -6117,13 +6143,13 @@
     @EnableFlags(FLAG_MODES_API)
     public void applyGlobalZenModeAsImplicitZenRule_modeOff_deactivatesImplicitRule() {
         mZenModeHelper.mConfig.automaticRules.clear();
-        mZenModeHelper.applyGlobalZenModeAsImplicitZenRule(mPkg, CUSTOM_PKG_UID,
+        mZenModeHelper.applyGlobalZenModeAsImplicitZenRule(UserHandle.CURRENT, mPkg, CUSTOM_PKG_UID,
                 ZEN_MODE_IMPORTANT_INTERRUPTIONS);
         assertThat(mZenModeHelper.mConfig.automaticRules).hasSize(1);
         assertThat(mZenModeHelper.mConfig.automaticRules.valueAt(0).condition.state)
                 .isEqualTo(STATE_TRUE);
 
-        mZenModeHelper.applyGlobalZenModeAsImplicitZenRule(mPkg, CUSTOM_PKG_UID,
+        mZenModeHelper.applyGlobalZenModeAsImplicitZenRule(UserHandle.CURRENT, mPkg, CUSTOM_PKG_UID,
                 ZEN_MODE_OFF);
 
         assertThat(mZenModeHelper.mConfig.automaticRules.valueAt(0).condition.state)
@@ -6135,8 +6161,8 @@
     public void applyGlobalZenModeAsImplicitZenRule_modeOffButNoPreviousRule_ignored() {
         mZenModeHelper.mConfig.automaticRules.clear();
 
-        mZenModeHelper.applyGlobalZenModeAsImplicitZenRule(CUSTOM_PKG_NAME, CUSTOM_PKG_UID,
-                ZEN_MODE_OFF);
+        mZenModeHelper.applyGlobalZenModeAsImplicitZenRule(UserHandle.CURRENT, CUSTOM_PKG_NAME,
+                CUSTOM_PKG_UID, ZEN_MODE_OFF);
 
         assertThat(mZenModeHelper.mConfig.automaticRules).isEmpty();
     }
@@ -6146,18 +6172,19 @@
     public void applyGlobalZenModeAsImplicitZenRule_update_unsnoozesRule() {
         mZenModeHelper.mConfig.automaticRules.clear();
 
-        mZenModeHelper.applyGlobalZenModeAsImplicitZenRule(CUSTOM_PKG_NAME, CUSTOM_PKG_UID,
-                ZEN_MODE_IMPORTANT_INTERRUPTIONS);
+        mZenModeHelper.applyGlobalZenModeAsImplicitZenRule(UserHandle.CURRENT, CUSTOM_PKG_NAME,
+                CUSTOM_PKG_UID, ZEN_MODE_IMPORTANT_INTERRUPTIONS);
         assertThat(mZenModeHelper.mConfig.automaticRules).hasSize(1);
         assertThat(mZenModeHelper.mConfig.automaticRules.valueAt(0).getConditionOverride())
                 .isEqualTo(OVERRIDE_NONE);
 
-        mZenModeHelper.setManualZenMode(ZEN_MODE_OFF, null, ORIGIN_APP, "test", "test", 0);
+        mZenModeHelper.setManualZenMode(UserHandle.CURRENT, ZEN_MODE_OFF, null, ORIGIN_APP, "test",
+                "test", 0);
         assertThat(mZenModeHelper.mConfig.automaticRules.valueAt(0).getConditionOverride())
                 .isEqualTo(OVERRIDE_DEACTIVATE);
 
-        mZenModeHelper.applyGlobalZenModeAsImplicitZenRule(CUSTOM_PKG_NAME, CUSTOM_PKG_UID,
-                ZEN_MODE_ALARMS);
+        mZenModeHelper.applyGlobalZenModeAsImplicitZenRule(UserHandle.CURRENT, CUSTOM_PKG_NAME,
+                CUSTOM_PKG_UID, ZEN_MODE_ALARMS);
 
         assertThat(mZenModeHelper.mConfig.automaticRules.valueAt(0).getConditionOverride())
                 .isEqualTo(OVERRIDE_NONE);
@@ -6171,9 +6198,8 @@
         mZenModeHelper.mConfig.automaticRules.clear();
 
         withoutWtfCrash(
-                () -> mZenModeHelper.applyGlobalZenModeAsImplicitZenRule(CUSTOM_PKG_NAME,
-                        CUSTOM_PKG_UID,
-                        ZEN_MODE_IMPORTANT_INTERRUPTIONS));
+                () -> mZenModeHelper.applyGlobalZenModeAsImplicitZenRule(UserHandle.CURRENT,
+                        CUSTOM_PKG_NAME, CUSTOM_PKG_UID, ZEN_MODE_IMPORTANT_INTERRUPTIONS));
 
         assertThat(mZenModeHelper.mConfig.automaticRules).isEmpty();
     }
@@ -6186,7 +6212,8 @@
         Policy policy = new Policy(PRIORITY_CATEGORY_CALLS | PRIORITY_CATEGORY_CONVERSATIONS,
                 PRIORITY_SENDERS_CONTACTS, PRIORITY_SENDERS_STARRED,
                 Policy.getAllSuppressedVisualEffects(), CONVERSATION_SENDERS_IMPORTANT);
-        mZenModeHelper.applyGlobalPolicyAsImplicitZenRule(CUSTOM_PKG_NAME, CUSTOM_PKG_UID, policy);
+        mZenModeHelper.applyGlobalPolicyAsImplicitZenRule(UserHandle.CURRENT, CUSTOM_PKG_NAME,
+                CUSTOM_PKG_UID, policy);
 
         ZenPolicy expectedZenPolicy = new ZenPolicy.Builder()
                 .disallowAllSounds()
@@ -6210,14 +6237,15 @@
         Policy original = new Policy(PRIORITY_CATEGORY_CALLS | PRIORITY_CATEGORY_CONVERSATIONS,
                 PRIORITY_SENDERS_CONTACTS, PRIORITY_SENDERS_STARRED,
                 Policy.getAllSuppressedVisualEffects(), CONVERSATION_SENDERS_IMPORTANT);
-        mZenModeHelper.applyGlobalPolicyAsImplicitZenRule(CUSTOM_PKG_NAME, CUSTOM_PKG_UID,
-                original);
+        mZenModeHelper.applyGlobalPolicyAsImplicitZenRule(UserHandle.CURRENT, CUSTOM_PKG_NAME,
+                CUSTOM_PKG_UID, original);
 
         // Change priorityCallSenders: contacts -> starred.
         Policy updated = new Policy(PRIORITY_CATEGORY_CALLS | PRIORITY_CATEGORY_CONVERSATIONS,
                 PRIORITY_SENDERS_STARRED, PRIORITY_SENDERS_STARRED,
                 Policy.getAllSuppressedVisualEffects(), CONVERSATION_SENDERS_IMPORTANT);
-        mZenModeHelper.applyGlobalPolicyAsImplicitZenRule(CUSTOM_PKG_NAME, CUSTOM_PKG_UID, updated);
+        mZenModeHelper.applyGlobalPolicyAsImplicitZenRule(UserHandle.CURRENT, CUSTOM_PKG_NAME,
+                CUSTOM_PKG_UID, updated);
 
         ZenPolicy expectedZenPolicy = new ZenPolicy.Builder()
                 .disallowAllSounds()
@@ -6241,7 +6269,8 @@
 
         // From app, call "setNotificationPolicy" and create and implicit rule.
         Policy originalPolicy = new Policy(PRIORITY_CATEGORY_MEDIA, 0, 0);
-        mZenModeHelper.applyGlobalPolicyAsImplicitZenRule(pkg, CUSTOM_PKG_UID, originalPolicy);
+        mZenModeHelper.applyGlobalPolicyAsImplicitZenRule(UserHandle.CURRENT, pkg, CUSTOM_PKG_UID,
+                originalPolicy);
         String ruleId = getOnlyElement(mZenModeHelper.mConfig.automaticRules.keySet());
 
         // Store this for checking later.
@@ -6249,18 +6278,19 @@
                 mZenModeHelper.mConfig.getZenPolicy()).allowMedia(true).build();
 
         // From user, update that rule's policy.
-        AutomaticZenRule rule = mZenModeHelper.getAutomaticZenRule(ruleId);
+        AutomaticZenRule rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId);
         ZenPolicy userUpdateZenPolicy = new ZenPolicy.Builder().disallowAllSounds()
                 .allowAlarms(true).build();
         AutomaticZenRule userUpdateRule = new AutomaticZenRule.Builder(rule)
                 .setZenPolicy(userUpdateZenPolicy)
                 .build();
-        mZenModeHelper.updateAutomaticZenRule(ruleId, userUpdateRule, ORIGIN_USER_IN_SYSTEMUI,
-                "reason", SYSTEM_UID);
+        mZenModeHelper.updateAutomaticZenRule(UserHandle.CURRENT, ruleId, userUpdateRule,
+                ORIGIN_USER_IN_SYSTEMUI, "reason", SYSTEM_UID);
 
         // From app, call "setNotificationPolicy" again.
         Policy appUpdatePolicy = new Policy(PRIORITY_CATEGORY_SYSTEM, 0, 0);
-        mZenModeHelper.applyGlobalPolicyAsImplicitZenRule(pkg, CUSTOM_PKG_UID, appUpdatePolicy);
+        mZenModeHelper.applyGlobalPolicyAsImplicitZenRule(UserHandle.CURRENT, pkg, CUSTOM_PKG_UID,
+                appUpdatePolicy);
 
         // The app's update was ignored, and the user's update is still current.
         assertThat(mZenModeHelper.mConfig.automaticRules.values())
@@ -6281,7 +6311,8 @@
 
         // From app, call "setNotificationPolicy" and create and implicit rule.
         Policy originalPolicy = new Policy(PRIORITY_CATEGORY_MEDIA, 0, 0);
-        mZenModeHelper.applyGlobalPolicyAsImplicitZenRule(pkg, CUSTOM_PKG_UID, originalPolicy);
+        mZenModeHelper.applyGlobalPolicyAsImplicitZenRule(UserHandle.CURRENT, pkg, CUSTOM_PKG_UID,
+                originalPolicy);
         String ruleId = getOnlyElement(mZenModeHelper.mConfig.automaticRules.keySet());
 
         // Store this for checking later.
@@ -6289,16 +6320,17 @@
                 mZenModeHelper.mConfig.getZenPolicy()).allowMedia(true).build();
 
         // From user, update something in that rule, but not the ZenPolicy.
-        AutomaticZenRule rule = mZenModeHelper.getAutomaticZenRule(ruleId);
+        AutomaticZenRule rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId);
         AutomaticZenRule userUpdateRule = new AutomaticZenRule.Builder(rule)
                 .setName("Rule renamed, not touching policy")
                 .build();
-        mZenModeHelper.updateAutomaticZenRule(ruleId, userUpdateRule, ORIGIN_USER_IN_SYSTEMUI,
-                "reason", SYSTEM_UID);
+        mZenModeHelper.updateAutomaticZenRule(UserHandle.CURRENT, ruleId, userUpdateRule,
+                ORIGIN_USER_IN_SYSTEMUI, "reason", SYSTEM_UID);
 
         // From app, call "setNotificationPolicy" again.
         Policy appUpdatePolicy = new Policy(PRIORITY_CATEGORY_SYSTEM, 0, 0);
-        mZenModeHelper.applyGlobalPolicyAsImplicitZenRule(pkg, CUSTOM_PKG_UID, appUpdatePolicy);
+        mZenModeHelper.applyGlobalPolicyAsImplicitZenRule(UserHandle.CURRENT, pkg, CUSTOM_PKG_UID,
+                appUpdatePolicy);
 
         // The app's update was applied.
         ZenPolicy appsSecondZenPolicy = new ZenPolicy.Builder()
@@ -6316,8 +6348,8 @@
         mZenModeHelper.mConfig.automaticRules.clear();
 
         withoutWtfCrash(
-                () -> mZenModeHelper.applyGlobalPolicyAsImplicitZenRule(CUSTOM_PKG_NAME,
-                        CUSTOM_PKG_UID, new Policy(0, 0, 0)));
+                () -> mZenModeHelper.applyGlobalPolicyAsImplicitZenRule(UserHandle.CURRENT,
+                        CUSTOM_PKG_NAME, CUSTOM_PKG_UID, new Policy(0, 0, 0)));
 
         assertThat(mZenModeHelper.mConfig.automaticRules).isEmpty();
     }
@@ -6329,11 +6361,11 @@
                 PRIORITY_SENDERS_CONTACTS, PRIORITY_SENDERS_STARRED,
                 Policy.getAllSuppressedVisualEffects(), STATE_FALSE,
                 CONVERSATION_SENDERS_IMPORTANT);
-        mZenModeHelper.applyGlobalPolicyAsImplicitZenRule(CUSTOM_PKG_NAME, CUSTOM_PKG_UID,
-                writtenPolicy);
+        mZenModeHelper.applyGlobalPolicyAsImplicitZenRule(UserHandle.CURRENT, CUSTOM_PKG_NAME,
+                CUSTOM_PKG_UID, writtenPolicy);
 
         Policy readPolicy = mZenModeHelper.getNotificationPolicyFromImplicitZenRule(
-                CUSTOM_PKG_NAME);
+                UserHandle.CURRENT, CUSTOM_PKG_NAME);
 
         assertThat(readPolicy).isEqualTo(writtenPolicy);
     }
@@ -6343,15 +6375,15 @@
     @DisableFlags(FLAG_MODES_UI)
     public void getNotificationPolicyFromImplicitZenRule_ruleWithoutPolicy_copiesGlobalPolicy() {
         // Implicit rule will get the global policy at the time of rule creation.
-        mZenModeHelper.applyGlobalZenModeAsImplicitZenRule(CUSTOM_PKG_NAME, CUSTOM_PKG_UID,
-                ZEN_MODE_IMPORTANT_INTERRUPTIONS);
+        mZenModeHelper.applyGlobalZenModeAsImplicitZenRule(UserHandle.CURRENT, CUSTOM_PKG_NAME,
+                CUSTOM_PKG_UID, ZEN_MODE_IMPORTANT_INTERRUPTIONS);
 
         // If the policy then changes afterwards, it should inherit updates because user cannot
         // edit the policy in the UI.
-        mZenModeHelper.setNotificationPolicy(new Policy(PRIORITY_CATEGORY_ALARMS, 0, 0),
-                ORIGIN_APP, 1);
+        mZenModeHelper.setNotificationPolicy(UserHandle.CURRENT,
+                new Policy(PRIORITY_CATEGORY_ALARMS, 0, 0), ORIGIN_APP, 1);
         Policy readPolicy = mZenModeHelper.getNotificationPolicyFromImplicitZenRule(
-                CUSTOM_PKG_NAME);
+                UserHandle.CURRENT, CUSTOM_PKG_NAME);
 
         assertThat(readPolicy).isNotNull();
         assertThat(readPolicy.allowCalls()).isFalse();
@@ -6362,10 +6394,11 @@
     @EnableFlags(FLAG_MODES_API)
     public void getNotificationPolicyFromImplicitZenRule_noImplicitRule_returnsGlobalPolicy() {
         Policy policy = new Policy(PRIORITY_CATEGORY_CALLS, PRIORITY_SENDERS_STARRED, 0);
-        mZenModeHelper.setNotificationPolicy(policy, ORIGIN_APP, CUSTOM_PKG_UID);
+        mZenModeHelper.setNotificationPolicy(UserHandle.CURRENT, policy, ORIGIN_APP,
+                CUSTOM_PKG_UID);
 
         Policy readPolicy = mZenModeHelper.getNotificationPolicyFromImplicitZenRule(
-                CUSTOM_PKG_NAME);
+                UserHandle.CURRENT, CUSTOM_PKG_NAME);
 
         assertThat(readPolicy).isNotNull();
         assertThat(readPolicy.allowCalls()).isTrue();
@@ -6398,7 +6431,8 @@
                 ZEN_MODE_IMPORTANT_INTERRUPTIONS, previousManualZenPolicy);
 
         Policy newManualPolicy = new Policy(PRIORITY_CATEGORY_EVENTS, 0, 0);
-        mZenModeHelper.setNotificationPolicy(newManualPolicy, ORIGIN_APP, CUSTOM_PKG_UID);
+        mZenModeHelper.setNotificationPolicy(UserHandle.CURRENT, newManualPolicy, ORIGIN_APP,
+                CUSTOM_PKG_UID);
         ZenPolicy newManualZenPolicy = ZenAdapters.notificationPolicyToZenPolicy(newManualPolicy);
 
         // Only app rules with default or same-as-manual policies were updated.
@@ -6426,10 +6460,12 @@
         when(mResources.getResourceName(resourceId)).thenReturn(veryLongResourceName);
         when(mResources.getIdentifier(veryLongResourceName, null, null)).thenReturn(resourceId);
 
-        String ruleId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(),
+        String ruleId = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT,
+                mContext.getPackageName(),
                 new AutomaticZenRule.Builder("Rule", CONDITION_ID).setIconResId(resourceId).build(),
                 ORIGIN_APP, "reason", CUSTOM_PKG_UID);
-        AutomaticZenRule storedRule = mZenModeHelper.getAutomaticZenRule(ruleId);
+        AutomaticZenRule storedRule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT,
+                ruleId);
 
         assertThat(storedRule.getIconResId()).isEqualTo(0);
     }
@@ -6440,8 +6476,8 @@
         ZenDeviceEffects effects = new ZenDeviceEffects.Builder()
                 .setShouldDimWallpaper(true)
                 .build();
-        mZenModeHelper.setManualZenRuleDeviceEffects(effects, ORIGIN_USER_IN_SYSTEMUI, "settings",
-                SYSTEM_UID);
+        mZenModeHelper.setManualZenRuleDeviceEffects(UserHandle.CURRENT, effects,
+                ORIGIN_USER_IN_SYSTEMUI, "settings", SYSTEM_UID);
 
         assertThat(mZenModeHelper.getConfig().manualRule).isNotNull();
         assertThat(mZenModeHelper.getConfig().isManualActive()).isFalse();
@@ -6451,14 +6487,14 @@
     @Test
     @EnableFlags({FLAG_MODES_API, FLAG_MODES_UI})
     public void setManualZenRuleDeviceEffects_preexistingMode() {
-        mZenModeHelper.setManualZenMode(ZEN_MODE_OFF, Uri.EMPTY, ORIGIN_USER_IN_SYSTEMUI,
-                "create manual rule", "settings", SYSTEM_UID);
+        mZenModeHelper.setManualZenMode(UserHandle.CURRENT, ZEN_MODE_OFF, Uri.EMPTY,
+                ORIGIN_USER_IN_SYSTEMUI, "create manual rule", "settings", SYSTEM_UID);
 
         ZenDeviceEffects effects = new ZenDeviceEffects.Builder()
                 .setShouldDimWallpaper(true)
                 .build();
-        mZenModeHelper.setManualZenRuleDeviceEffects(effects, ORIGIN_USER_IN_SYSTEMUI, "settings",
-                SYSTEM_UID);
+        mZenModeHelper.setManualZenRuleDeviceEffects(UserHandle.CURRENT, effects,
+                ORIGIN_USER_IN_SYSTEMUI, "settings", SYSTEM_UID);
 
         assertThat(mZenModeHelper.getConfig().manualRule).isNotNull();
         assertThat(mZenModeHelper.getConfig().isManualActive()).isFalse();
@@ -6473,7 +6509,7 @@
                 .setEnabled(false)
                 .build();
 
-        String ruleId = mZenModeHelper.addAutomaticZenRule(mPkg, startsDisabled,
+        String ruleId = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT, mPkg, startsDisabled,
                 ORIGIN_APP,
                 "new", CUSTOM_PKG_UID);
 
@@ -6488,7 +6524,7 @@
                 .setOwner(new ComponentName(mPkg, "SomeProvider"))
                 .setEnabled(true)
                 .build();
-        String ruleId = mZenModeHelper.addAutomaticZenRule(mPkg, startsEnabled,
+        String ruleId = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT, mPkg, startsEnabled,
                 ORIGIN_APP,
                 "new", CUSTOM_PKG_UID);
         assertThat(mZenModeHelper.mConfig.automaticRules.get(ruleId).disabledOrigin).isEqualTo(
@@ -6497,8 +6533,8 @@
         AutomaticZenRule nowDisabled = new AutomaticZenRule.Builder(startsEnabled)
                 .setEnabled(false)
                 .build();
-        mZenModeHelper.updateAutomaticZenRule(ruleId, nowDisabled, ORIGIN_USER_IN_SYSTEMUI, "off",
-                SYSTEM_UID);
+        mZenModeHelper.updateAutomaticZenRule(UserHandle.CURRENT, ruleId, nowDisabled,
+                ORIGIN_USER_IN_SYSTEMUI, "off", SYSTEM_UID);
 
         assertThat(mZenModeHelper.mConfig.automaticRules.get(ruleId).disabledOrigin).isEqualTo(
                 ORIGIN_USER_IN_SYSTEMUI);
@@ -6511,14 +6547,14 @@
                 .setOwner(new ComponentName(mPkg, "SomeProvider"))
                 .setEnabled(true)
                 .build();
-        String ruleId = mZenModeHelper.addAutomaticZenRule(mPkg, startsEnabled,
+        String ruleId = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT, mPkg, startsEnabled,
                 ORIGIN_APP,
                 "new", CUSTOM_PKG_UID);
         AutomaticZenRule nowDisabled = new AutomaticZenRule.Builder(startsEnabled)
                 .setEnabled(false)
                 .build();
-        mZenModeHelper.updateAutomaticZenRule(ruleId, nowDisabled, ORIGIN_USER_IN_SYSTEMUI, "off",
-                SYSTEM_UID);
+        mZenModeHelper.updateAutomaticZenRule(UserHandle.CURRENT, ruleId, nowDisabled,
+                ORIGIN_USER_IN_SYSTEMUI, "off", SYSTEM_UID);
         assertThat(mZenModeHelper.mConfig.automaticRules.get(ruleId).disabledOrigin).isEqualTo(
                 ORIGIN_USER_IN_SYSTEMUI);
 
@@ -6526,8 +6562,8 @@
         AutomaticZenRule nowRenamed = new AutomaticZenRule.Builder(nowDisabled)
                 .setName("Fancy pants rule")
                 .build();
-        mZenModeHelper.updateAutomaticZenRule(ruleId, nowRenamed, ORIGIN_APP, "update",
-                CUSTOM_PKG_UID);
+        mZenModeHelper.updateAutomaticZenRule(UserHandle.CURRENT, ruleId, nowRenamed, ORIGIN_APP,
+                "update", CUSTOM_PKG_UID);
 
         // Identity of the disabler is preserved.
         assertThat(mZenModeHelper.mConfig.automaticRules.get(ruleId).disabledOrigin).isEqualTo(
@@ -6541,14 +6577,14 @@
                 .setOwner(new ComponentName(mPkg, "SomeProvider"))
                 .setEnabled(true)
                 .build();
-        String ruleId = mZenModeHelper.addAutomaticZenRule(mPkg, startsEnabled,
+        String ruleId = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT, mPkg, startsEnabled,
                 ORIGIN_APP,
                 "new", CUSTOM_PKG_UID);
         AutomaticZenRule nowDisabled = new AutomaticZenRule.Builder(startsEnabled)
                 .setEnabled(false)
                 .build();
-        mZenModeHelper.updateAutomaticZenRule(ruleId, nowDisabled, ORIGIN_USER_IN_SYSTEMUI, "off",
-                SYSTEM_UID);
+        mZenModeHelper.updateAutomaticZenRule(UserHandle.CURRENT, ruleId, nowDisabled,
+                ORIGIN_USER_IN_SYSTEMUI, "off", SYSTEM_UID);
         assertThat(mZenModeHelper.mConfig.automaticRules.get(ruleId).disabledOrigin).isEqualTo(
                 ORIGIN_USER_IN_SYSTEMUI);
 
@@ -6556,8 +6592,8 @@
         AutomaticZenRule nowEnabled = new AutomaticZenRule.Builder(nowDisabled)
                 .setEnabled(true)
                 .build();
-        mZenModeHelper.updateAutomaticZenRule(ruleId, nowEnabled, ORIGIN_APP, "on",
-                CUSTOM_PKG_UID);
+        mZenModeHelper.updateAutomaticZenRule(UserHandle.CURRENT, ruleId, nowEnabled, ORIGIN_APP,
+                "on", CUSTOM_PKG_UID);
 
         // Identity of the disabler was cleared.
         assertThat(mZenModeHelper.mConfig.automaticRules.get(ruleId).disabledOrigin).isEqualTo(
@@ -6570,10 +6606,10 @@
         AutomaticZenRule rule = new AutomaticZenRule.Builder("Rule", Uri.parse("cond"))
                 .setPackage(mPkg)
                 .build();
-        String ruleId = mZenModeHelper.addAutomaticZenRule(mPkg, rule, ORIGIN_APP, "adding",
-                CUSTOM_PKG_UID);
+        String ruleId = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT, mPkg, rule,
+                ORIGIN_APP, "adding", CUSTOM_PKG_UID);
 
-        mZenModeHelper.setAutomaticZenRuleState(ruleId,
+        mZenModeHelper.setAutomaticZenRuleState(UserHandle.CURRENT, ruleId,
                 new Condition(rule.getConditionId(), "manual-on", STATE_TRUE, SOURCE_USER_ACTION),
                 ORIGIN_USER_IN_SYSTEMUI, SYSTEM_UID);
 
@@ -6588,13 +6624,13 @@
         AutomaticZenRule rule = new AutomaticZenRule.Builder("Rule", Uri.parse("cond"))
                 .setPackage(mPkg)
                 .build();
-        String ruleId = mZenModeHelper.addAutomaticZenRule(mPkg, rule, ORIGIN_APP, "adding",
-                CUSTOM_PKG_UID);
+        String ruleId = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT, mPkg, rule,
+                ORIGIN_APP, "adding", CUSTOM_PKG_UID);
         Condition autoOn = new Condition(rule.getConditionId(), "auto-on", STATE_TRUE,
                 SOURCE_CONTEXT);
         ZenRule zenRule;
 
-        mZenModeHelper.setAutomaticZenRuleState(ruleId,
+        mZenModeHelper.setAutomaticZenRuleState(UserHandle.CURRENT, ruleId,
                 new Condition(rule.getConditionId(), "manual-on", STATE_TRUE, SOURCE_USER_ACTION),
                 ORIGIN_USER_IN_SYSTEMUI, SYSTEM_UID);
         zenRule = mZenModeHelper.mConfig.automaticRules.get(ruleId);
@@ -6602,7 +6638,7 @@
         assertThat(zenRule.getConditionOverride()).isEqualTo(OVERRIDE_ACTIVATE);
         assertThat(zenRule.condition).isNull();
 
-        mZenModeHelper.setAutomaticZenRuleState(ruleId,
+        mZenModeHelper.setAutomaticZenRuleState(UserHandle.CURRENT, ruleId,
                 new Condition(rule.getConditionId(), "manual-off", STATE_FALSE, SOURCE_USER_ACTION),
                 ORIGIN_USER_IN_SYSTEMUI, SYSTEM_UID);
         zenRule = mZenModeHelper.mConfig.automaticRules.get(ruleId);
@@ -6611,7 +6647,8 @@
         assertThat(zenRule.condition).isNull();
 
         // Bonus check: app has resumed control over the rule and can now turn it on.
-        mZenModeHelper.setAutomaticZenRuleState(ruleId, autoOn,  ORIGIN_APP, CUSTOM_PKG_UID);
+        mZenModeHelper.setAutomaticZenRuleState(UserHandle.CURRENT, ruleId, autoOn, ORIGIN_APP,
+                CUSTOM_PKG_UID);
         zenRule = mZenModeHelper.mConfig.automaticRules.get(ruleId);
         assertThat(zenRule.isActive()).isTrue();
         assertThat(zenRule.getConditionOverride()).isEqualTo(OVERRIDE_NONE);
@@ -6624,21 +6661,22 @@
         AutomaticZenRule rule = new AutomaticZenRule.Builder("Rule", Uri.parse("cond"))
                 .setPackage(mPkg)
                 .build();
-        String ruleId = mZenModeHelper.addAutomaticZenRule(mPkg, rule, ORIGIN_APP, "adding",
-                CUSTOM_PKG_UID);
+        String ruleId = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT, mPkg, rule,
+                ORIGIN_APP, "adding", CUSTOM_PKG_UID);
         Condition autoOn = new Condition(rule.getConditionId(), "auto-on", STATE_TRUE,
                 SOURCE_CONTEXT);
         Condition autoOff = new Condition(rule.getConditionId(), "auto-off", STATE_FALSE,
                 SOURCE_CONTEXT);
         ZenRule zenRule;
 
-        mZenModeHelper.setAutomaticZenRuleState(ruleId, autoOn,  ORIGIN_APP, CUSTOM_PKG_UID);
+        mZenModeHelper.setAutomaticZenRuleState(UserHandle.CURRENT, ruleId, autoOn, ORIGIN_APP,
+                CUSTOM_PKG_UID);
         zenRule = mZenModeHelper.mConfig.automaticRules.get(ruleId);
         assertThat(zenRule.isActive()).isTrue();
         assertThat(zenRule.getConditionOverride()).isEqualTo(OVERRIDE_NONE);
         assertThat(zenRule.condition).isEqualTo(autoOn);
 
-        mZenModeHelper.setAutomaticZenRuleState(ruleId,
+        mZenModeHelper.setAutomaticZenRuleState(UserHandle.CURRENT, ruleId,
                 new Condition(rule.getConditionId(), "manual-off", STATE_FALSE, SOURCE_USER_ACTION),
                 ORIGIN_USER_IN_SYSTEMUI, SYSTEM_UID);
         zenRule = mZenModeHelper.mConfig.automaticRules.get(ruleId);
@@ -6646,7 +6684,7 @@
         assertThat(zenRule.getConditionOverride()).isEqualTo(OVERRIDE_DEACTIVATE);
         assertThat(zenRule.condition).isEqualTo(autoOn);
 
-        mZenModeHelper.setAutomaticZenRuleState(ruleId,
+        mZenModeHelper.setAutomaticZenRuleState(UserHandle.CURRENT, ruleId,
                 new Condition(rule.getConditionId(), "manual-on", STATE_TRUE, SOURCE_USER_ACTION),
                 ORIGIN_USER_IN_SYSTEMUI, SYSTEM_UID);
         zenRule = mZenModeHelper.mConfig.automaticRules.get(ruleId);
@@ -6655,7 +6693,8 @@
         assertThat(zenRule.condition).isEqualTo(autoOn);
 
         // Bonus check: app has resumed control over the rule and can now turn it off.
-        mZenModeHelper.setAutomaticZenRuleState(ruleId, autoOff,  ORIGIN_APP, CUSTOM_PKG_UID);
+        mZenModeHelper.setAutomaticZenRuleState(UserHandle.CURRENT, ruleId, autoOff, ORIGIN_APP,
+                CUSTOM_PKG_UID);
         zenRule = mZenModeHelper.mConfig.automaticRules.get(ruleId);
         assertThat(zenRule.isActive()).isFalse();
         assertThat(zenRule.getConditionOverride()).isEqualTo(OVERRIDE_NONE);
@@ -6668,10 +6707,10 @@
         AutomaticZenRule rule = new AutomaticZenRule.Builder("Rule", Uri.parse("cond"))
                 .setPackage(mPkg)
                 .build();
-        String ruleId = mZenModeHelper.addAutomaticZenRule(mPkg, rule, ORIGIN_APP, "adding",
-                CUSTOM_PKG_UID);
+        String ruleId = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT, mPkg, rule,
+                ORIGIN_APP, "adding", CUSTOM_PKG_UID);
 
-        mZenModeHelper.setAutomaticZenRuleState(ruleId,
+        mZenModeHelper.setAutomaticZenRuleState(UserHandle.CURRENT, ruleId,
                 new Condition(rule.getConditionId(), "auto-on", STATE_TRUE, SOURCE_CONTEXT),
                 ORIGIN_APP, CUSTOM_PKG_UID);
         ZenRule zenRuleOn = mZenModeHelper.mConfig.automaticRules.get(ruleId);
@@ -6679,7 +6718,7 @@
         assertThat(zenRuleOn.getConditionOverride()).isEqualTo(OVERRIDE_NONE);
         assertThat(zenRuleOn.condition).isNotNull();
 
-        mZenModeHelper.setAutomaticZenRuleState(ruleId,
+        mZenModeHelper.setAutomaticZenRuleState(UserHandle.CURRENT, ruleId,
                 new Condition(rule.getConditionId(), "manual-off", STATE_FALSE, SOURCE_USER_ACTION),
                 ORIGIN_USER_IN_SYSTEMUI, SYSTEM_UID);
         ZenRule zenRuleOff = mZenModeHelper.mConfig.automaticRules.get(ruleId);
@@ -6694,31 +6733,31 @@
         AutomaticZenRule rule = new AutomaticZenRule.Builder("Rule", Uri.parse("cond"))
                 .setPackage(mPkg)
                 .build();
-        String ruleId = mZenModeHelper.addAutomaticZenRule(mPkg, rule, ORIGIN_APP, "adding",
-                CUSTOM_PKG_UID);
+        String ruleId = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT, mPkg, rule,
+                ORIGIN_APP, "adding", CUSTOM_PKG_UID);
         ZenRule zenRule;
 
-        mZenModeHelper.setAutomaticZenRuleState(ruleId,
+        mZenModeHelper.setAutomaticZenRuleState(UserHandle.CURRENT, ruleId,
                 new Condition(rule.getConditionId(), "manual-on", STATE_TRUE, SOURCE_USER_ACTION),
                 ORIGIN_USER_IN_SYSTEMUI, SYSTEM_UID);
         zenRule = mZenModeHelper.mConfig.automaticRules.get(ruleId);
         assertThat(zenRule.isActive()).isTrue();
         assertThat(zenRule.getConditionOverride()).isEqualTo(OVERRIDE_ACTIVATE);
 
-        mZenModeHelper.setAutomaticZenRuleState(ruleId,
+        mZenModeHelper.setAutomaticZenRuleState(UserHandle.CURRENT, ruleId,
                 new Condition(rule.getConditionId(), "auto-off", STATE_FALSE, SOURCE_CONTEXT),
                 ORIGIN_APP, CUSTOM_PKG_UID);
         zenRule = mZenModeHelper.mConfig.automaticRules.get(ruleId);
         assertThat(zenRule.isActive()).isTrue();
         assertThat(zenRule.getConditionOverride()).isEqualTo(OVERRIDE_ACTIVATE);
 
-        mZenModeHelper.setAutomaticZenRuleState(ruleId,
+        mZenModeHelper.setAutomaticZenRuleState(UserHandle.CURRENT, ruleId,
                 new Condition(rule.getConditionId(), "auto-on", STATE_TRUE, SOURCE_CONTEXT),
                 ORIGIN_APP, CUSTOM_PKG_UID);
         zenRule = mZenModeHelper.mConfig.automaticRules.get(ruleId);
         assertThat(zenRule.isActive()).isTrue();
         assertThat(zenRule.getConditionOverride()).isEqualTo(OVERRIDE_NONE);
-        mZenModeHelper.setAutomaticZenRuleState(ruleId,
+        mZenModeHelper.setAutomaticZenRuleState(UserHandle.CURRENT, ruleId,
                 new Condition(rule.getConditionId(), "auto-off", STATE_FALSE, SOURCE_CONTEXT),
                 ORIGIN_APP, CUSTOM_PKG_UID);
         zenRule = mZenModeHelper.mConfig.automaticRules.get(ruleId);
@@ -6731,39 +6770,39 @@
         AutomaticZenRule rule = new AutomaticZenRule.Builder("Rule", Uri.parse("cond"))
                 .setPackage(mPkg)
                 .build();
-        String ruleId = mZenModeHelper.addAutomaticZenRule(mPkg, rule, ORIGIN_APP, "adding",
-                CUSTOM_PKG_UID);
+        String ruleId = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT, mPkg, rule,
+                ORIGIN_APP, "adding", CUSTOM_PKG_UID);
         ZenRule zenRule;
 
-        mZenModeHelper.setAutomaticZenRuleState(ruleId,
+        mZenModeHelper.setAutomaticZenRuleState(UserHandle.CURRENT, ruleId,
                 new Condition(rule.getConditionId(), "auto-on", STATE_TRUE, SOURCE_CONTEXT),
                 ORIGIN_APP, CUSTOM_PKG_UID);
         zenRule = mZenModeHelper.mConfig.automaticRules.get(ruleId);
         assertThat(zenRule.isActive()).isTrue();
         assertThat(zenRule.getConditionOverride()).isEqualTo(OVERRIDE_NONE);
 
-        mZenModeHelper.setAutomaticZenRuleState(ruleId,
+        mZenModeHelper.setAutomaticZenRuleState(UserHandle.CURRENT, ruleId,
                 new Condition(rule.getConditionId(), "manual-off", STATE_FALSE, SOURCE_USER_ACTION),
                 ORIGIN_USER_IN_SYSTEMUI, SYSTEM_UID);
         zenRule = mZenModeHelper.mConfig.automaticRules.get(ruleId);
         assertThat(zenRule.isActive()).isFalse();
         assertThat(zenRule.getConditionOverride()).isEqualTo(OVERRIDE_DEACTIVATE);
 
-        mZenModeHelper.setAutomaticZenRuleState(ruleId,
+        mZenModeHelper.setAutomaticZenRuleState(UserHandle.CURRENT, ruleId,
                 new Condition(rule.getConditionId(), "auto-on", STATE_TRUE, SOURCE_CONTEXT),
                 ORIGIN_APP, CUSTOM_PKG_UID);
         zenRule = mZenModeHelper.mConfig.automaticRules.get(ruleId);
         assertThat(zenRule.isActive()).isFalse();
         assertThat(zenRule.getConditionOverride()).isEqualTo(OVERRIDE_DEACTIVATE);
 
-        mZenModeHelper.setAutomaticZenRuleState(ruleId,
+        mZenModeHelper.setAutomaticZenRuleState(UserHandle.CURRENT, ruleId,
                 new Condition(rule.getConditionId(), "auto-off", STATE_FALSE, SOURCE_CONTEXT),
                 ORIGIN_APP, CUSTOM_PKG_UID);
         zenRule = mZenModeHelper.mConfig.automaticRules.get(ruleId);
         assertThat(zenRule.isActive()).isFalse();
         assertThat(zenRule.getConditionOverride()).isEqualTo(OVERRIDE_NONE);
 
-        mZenModeHelper.setAutomaticZenRuleState(ruleId,
+        mZenModeHelper.setAutomaticZenRuleState(UserHandle.CURRENT, ruleId,
                 new Condition(rule.getConditionId(), "auto-on", STATE_TRUE, SOURCE_CONTEXT),
                 ORIGIN_APP, CUSTOM_PKG_UID);
         zenRule = mZenModeHelper.mConfig.automaticRules.get(ruleId);
@@ -6777,18 +6816,18 @@
         AutomaticZenRule rule = new AutomaticZenRule.Builder("Rule", Uri.parse("cond"))
                 .setPackage(mPkg)
                 .build();
-        String ruleId = mZenModeHelper.addAutomaticZenRule(mPkg, rule, ORIGIN_APP, "adding",
-                CUSTOM_PKG_UID);
+        String ruleId = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT, mPkg, rule,
+                ORIGIN_APP, "adding", CUSTOM_PKG_UID);
 
         // User manually turns on rule from SysUI / Settings...
-        mZenModeHelper.setAutomaticZenRuleState(ruleId,
+        mZenModeHelper.setAutomaticZenRuleState(UserHandle.CURRENT, ruleId,
                 new Condition(rule.getConditionId(), "manual-on-from-sysui", STATE_TRUE,
                         SOURCE_USER_ACTION), ORIGIN_USER_IN_SYSTEMUI, SYSTEM_UID);
         assertThat(getZenRule(ruleId).isActive()).isTrue();
         assertThat(getZenRule(ruleId).getConditionOverride()).isEqualTo(OVERRIDE_ACTIVATE);
 
         // ... and they can turn it off manually from inside the app.
-        mZenModeHelper.setAutomaticZenRuleState(ruleId,
+        mZenModeHelper.setAutomaticZenRuleState(UserHandle.CURRENT, ruleId,
                 new Condition(rule.getConditionId(), "manual-off-from-app", STATE_FALSE,
                         SOURCE_USER_ACTION), ORIGIN_USER_IN_APP, CUSTOM_PKG_UID);
         assertThat(getZenRule(ruleId).isActive()).isFalse();
@@ -6801,25 +6840,25 @@
         AutomaticZenRule rule = new AutomaticZenRule.Builder("Rule", Uri.parse("cond"))
                 .setPackage(mPkg)
                 .build();
-        String ruleId = mZenModeHelper.addAutomaticZenRule(mPkg, rule, ORIGIN_APP, "adding",
-                CUSTOM_PKG_UID);
+        String ruleId = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT, mPkg, rule,
+                ORIGIN_APP, "adding", CUSTOM_PKG_UID);
 
         // Rule is activated due to its schedule.
-        mZenModeHelper.setAutomaticZenRuleState(ruleId,
+        mZenModeHelper.setAutomaticZenRuleState(UserHandle.CURRENT, ruleId,
                 new Condition(rule.getConditionId(), "auto-on-from-app", STATE_TRUE,
                         SOURCE_SCHEDULE), ORIGIN_APP, CUSTOM_PKG_UID);
         assertThat(getZenRule(ruleId).isActive()).isTrue();
         assertThat(getZenRule(ruleId).getConditionOverride()).isEqualTo(OVERRIDE_NONE);
 
         // User manually turns off rule from SysUI / Settings...
-        mZenModeHelper.setAutomaticZenRuleState(ruleId,
+        mZenModeHelper.setAutomaticZenRuleState(UserHandle.CURRENT, ruleId,
                 new Condition(rule.getConditionId(), "manual-off-from-sysui", STATE_FALSE,
                         SOURCE_USER_ACTION), ORIGIN_USER_IN_SYSTEMUI, SYSTEM_UID);
         assertThat(getZenRule(ruleId).isActive()).isFalse();
         assertThat(getZenRule(ruleId).getConditionOverride()).isEqualTo(OVERRIDE_DEACTIVATE);
 
         // ... and they can turn it on manually from inside the app.
-        mZenModeHelper.setAutomaticZenRuleState(ruleId,
+        mZenModeHelper.setAutomaticZenRuleState(UserHandle.CURRENT, ruleId,
                 new Condition(rule.getConditionId(), "manual-on-from-app", STATE_TRUE,
                         SOURCE_USER_ACTION), ORIGIN_USER_IN_APP, CUSTOM_PKG_UID);
         assertThat(getZenRule(ruleId).isActive()).isTrue();
@@ -6832,19 +6871,19 @@
         AutomaticZenRule rule = new AutomaticZenRule.Builder("Rule", Uri.parse("cond"))
                 .setPackage(mPkg)
                 .build();
-        String ruleId = mZenModeHelper.addAutomaticZenRule(mPkg, rule, ORIGIN_APP, "adding",
-                CUSTOM_PKG_UID);
+        String ruleId = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT, mPkg, rule,
+                ORIGIN_APP, "adding", CUSTOM_PKG_UID);
 
         // Rule is manually activated by the user in the app.
         // This turns the rule on, but is NOT an override...
-        mZenModeHelper.setAutomaticZenRuleState(ruleId,
+        mZenModeHelper.setAutomaticZenRuleState(UserHandle.CURRENT, ruleId,
                 new Condition(rule.getConditionId(), "manual-on-from-app", STATE_TRUE,
                         SOURCE_USER_ACTION), ORIGIN_USER_IN_APP, CUSTOM_PKG_UID);
         assertThat(getZenRule(ruleId).isActive()).isTrue();
         assertThat(getZenRule(ruleId).getConditionOverride()).isEqualTo(OVERRIDE_NONE);
 
         // ... so the app can turn it off when its schedule is over.
-        mZenModeHelper.setAutomaticZenRuleState(ruleId,
+        mZenModeHelper.setAutomaticZenRuleState(UserHandle.CURRENT, ruleId,
                 new Condition(rule.getConditionId(), "auto-off-from-app", STATE_FALSE,
                         SOURCE_SCHEDULE), ORIGIN_APP, CUSTOM_PKG_UID);
         assertThat(getZenRule(ruleId).isActive()).isFalse();
@@ -6909,12 +6948,13 @@
         AutomaticZenRule rule = new AutomaticZenRule.Builder("Rule", Uri.parse("cond"))
                 .setPackage(mPkg)
                 .build();
-        String ruleId = mZenModeHelper.addAutomaticZenRule(mPkg, rule, ORIGIN_APP, "adding",
-                CUSTOM_PKG_UID);
-        mZenModeHelper.setAutomaticZenRuleState(ruleId,
+        String ruleId = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT, mPkg, rule,
+                ORIGIN_APP, "adding", CUSTOM_PKG_UID);
+        mZenModeHelper.setAutomaticZenRuleState(UserHandle.CURRENT, ruleId,
                 new Condition(rule.getConditionId(), "manual-on", STATE_TRUE, SOURCE_USER_ACTION),
                 ORIGIN_USER_IN_SYSTEMUI, SYSTEM_UID);
-        assertThat(mZenModeHelper.getAutomaticZenRuleState(ruleId)).isEqualTo(STATE_TRUE);
+        assertThat(mZenModeHelper.getAutomaticZenRuleState(UserHandle.CURRENT, ruleId))
+                .isEqualTo(STATE_TRUE);
         ZenRule zenRule = mZenModeHelper.mConfig.automaticRules.get(ruleId);
         assertThat(zenRule.getConditionOverride()).isEqualTo(OVERRIDE_ACTIVATE);
         assertThat(zenRule.condition).isNull();
@@ -6928,12 +6968,14 @@
         mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL);
 
         if (Flags.modesUi()) {
-            assertThat(mZenModeHelper.getAutomaticZenRuleState(ruleId)).isEqualTo(STATE_TRUE);
+            assertThat(mZenModeHelper.getAutomaticZenRuleState(UserHandle.CURRENT, ruleId))
+                    .isEqualTo(STATE_TRUE);
             zenRule = mZenModeHelper.mConfig.automaticRules.get(ruleId);
             assertThat(zenRule.getConditionOverride()).isEqualTo(OVERRIDE_ACTIVATE);
             assertThat(zenRule.condition).isNull();
         } else {
-            assertThat(mZenModeHelper.getAutomaticZenRuleState(ruleId)).isEqualTo(STATE_FALSE);
+            assertThat(mZenModeHelper.getAutomaticZenRuleState(UserHandle.CURRENT, ruleId))
+                    .isEqualTo(STATE_FALSE);
         }
     }
 
@@ -6944,15 +6986,16 @@
         AutomaticZenRule rule = new AutomaticZenRule.Builder("Rule", Uri.parse("cond"))
                 .setPackage(mPkg)
                 .build();
-        String ruleId = mZenModeHelper.addAutomaticZenRule(mPkg, rule, ORIGIN_APP, "adding",
-                CUSTOM_PKG_UID);
-        mZenModeHelper.setAutomaticZenRuleState(ruleId,
+        String ruleId = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT, mPkg, rule,
+                ORIGIN_APP, "adding", CUSTOM_PKG_UID);
+        mZenModeHelper.setAutomaticZenRuleState(UserHandle.CURRENT, ruleId,
                 new Condition(rule.getConditionId(), "auto-on", STATE_TRUE, SOURCE_CONTEXT),
                 ORIGIN_APP, CUSTOM_PKG_UID);
-        mZenModeHelper.setAutomaticZenRuleState(ruleId,
+        mZenModeHelper.setAutomaticZenRuleState(UserHandle.CURRENT, ruleId,
                 new Condition(rule.getConditionId(), "snooze", STATE_FALSE, SOURCE_USER_ACTION),
                 ORIGIN_USER_IN_SYSTEMUI, SYSTEM_UID);
-        assertThat(mZenModeHelper.getAutomaticZenRuleState(ruleId)).isEqualTo(STATE_FALSE);
+        assertThat(mZenModeHelper.getAutomaticZenRuleState(UserHandle.CURRENT, ruleId))
+                .isEqualTo(STATE_FALSE);
         ZenRule zenRule = mZenModeHelper.mConfig.automaticRules.get(ruleId);
         assertThat(zenRule.getConditionOverride()).isEqualTo(OVERRIDE_DEACTIVATE);
         assertThat(zenRule.condition).isNotNull();
@@ -6965,7 +7008,8 @@
         TypedXmlPullParser parser = getParserForByteStream(xmlBytes);
         mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL);
 
-        assertThat(mZenModeHelper.getAutomaticZenRuleState(ruleId)).isEqualTo(STATE_TRUE);
+        assertThat(mZenModeHelper.getAutomaticZenRuleState(UserHandle.CURRENT, ruleId))
+                .isEqualTo(STATE_TRUE);
         zenRule = mZenModeHelper.mConfig.automaticRules.get(ruleId);
         assertThat(zenRule.getConditionOverride()).isEqualTo(OVERRIDE_NONE);
         assertThat(zenRule.condition).isNotNull();
@@ -6978,8 +7022,8 @@
         AutomaticZenRule azr = new AutomaticZenRule.Builder("Rule", Uri.parse("cond"))
                 .setPackage(mPkg)
                 .build();
-        String ruleId = mZenModeHelper.addAutomaticZenRule(mPkg, azr, ORIGIN_APP, "adding",
-                CUSTOM_PKG_UID);
+        String ruleId = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT, mPkg, azr,
+                ORIGIN_APP, "adding", CUSTOM_PKG_UID);
 
         ZenRule zenRule = checkNotNull(mZenModeHelper.mConfig.automaticRules.get(ruleId));
 
@@ -7039,15 +7083,62 @@
                         .allowPriorityChannels(false)
                         .build();
 
-        mZenModeHelper.updateHasPriorityChannels(true);
-        assertThat(mZenModeHelper.getNotificationPolicy().hasPriorityChannels()).isTrue();
+        mZenModeHelper.updateHasPriorityChannels(UserHandle.CURRENT, true);
+        assertThat(mZenModeHelper.getNotificationPolicy(UserHandle.CURRENT).hasPriorityChannels())
+                .isTrue();
 
         // getNotificationPolicy() gets its policy from the manual rule; channels not permitted
-        assertThat(mZenModeHelper.getNotificationPolicy().allowPriorityChannels()).isFalse();
+        assertThat(mZenModeHelper.getNotificationPolicy(UserHandle.CURRENT).allowPriorityChannels())
+                .isFalse();
 
-        mZenModeHelper.updateHasPriorityChannels(false);
-        assertThat(mZenModeHelper.getNotificationPolicy().hasPriorityChannels()).isFalse();
-        assertThat(mZenModeHelper.getNotificationPolicy().allowPriorityChannels()).isFalse();
+        mZenModeHelper.updateHasPriorityChannels(UserHandle.CURRENT, false);
+        assertThat(mZenModeHelper.getNotificationPolicy(UserHandle.CURRENT).hasPriorityChannels())
+                .isFalse();
+        assertThat(mZenModeHelper.getNotificationPolicy(UserHandle.CURRENT).allowPriorityChannels())
+                .isFalse();
+    }
+
+    @Test
+    @EnableFlags(FLAG_MODES_MULTIUSER)
+    public void setManualZenMode_fromCurrentUser_updatesCurrentConfig() {
+        // Initialize default configurations (default rules) for both users.
+        mZenModeHelper.onUserSwitched(1);
+        mZenModeHelper.onUserSwitched(2);
+        UserHandle currentUser = UserHandle.of(2);
+        ZenModeHelper.Callback callback = mock(ZenModeHelper.Callback.class);
+        mZenModeHelper.addCallback(callback);
+
+        mZenModeHelper.setManualZenMode(currentUser, ZEN_MODE_ALARMS, null, ORIGIN_APP, "reason",
+                mPkg, CUSTOM_PKG_UID);
+
+        assertThat(mZenModeHelper.mConfig.isManualActive()).isTrue();
+        assertThat(mZenModeHelper.mConfigs.get(1).isManualActive()).isFalse();
+
+        // And we sent the broadcast announcing the change.
+        mTestableLooper.processAllMessages();
+        verify(callback).onZenModeChanged();
+    }
+
+    @Test
+    @EnableFlags(FLAG_MODES_MULTIUSER)
+    public void setInterruptionFilter_fromNonCurrentUser_updatesNonCurrentConfig() {
+        // Initialize default configurations (default rules) for both users.
+        // Afterwards, 2 is current, and 1 is background.
+        mZenModeHelper.onUserSwitched(1);
+        mZenModeHelper.onUserSwitched(2);
+        UserHandle backgroundUser = UserHandle.of(1);
+        ZenModeHelper.Callback callback = mock(ZenModeHelper.Callback.class);
+        mZenModeHelper.addCallback(callback);
+
+        mZenModeHelper.setManualZenMode(backgroundUser, ZEN_MODE_ALARMS, null, ORIGIN_APP, "reason",
+                mPkg, CUSTOM_PKG_UID);
+
+        assertThat(mZenModeHelper.mConfig.isManualActive()).isFalse();
+        assertThat(mZenModeHelper.mConfigs.get(1).isManualActive()).isTrue();
+
+        // And no broadcasts is sent for "background" changes (they were not evaluated).
+        mTestableLooper.processAllMessages();
+        verify(callback, never()).onZenModeChanged();
     }
 
     private static void addZenRule(ZenModeConfig config, String id, String ownerPkg, int zenMode,
@@ -7122,7 +7213,7 @@
                 SUPPRESSED_EFFECT_BADGE,
                 0,
                 CONVERSATION_SENDERS_IMPORTANT);
-        mZenModeHelper.setNotificationPolicy(customPolicy, ORIGIN_UNKNOWN, 1);
+        mZenModeHelper.setNotificationPolicy(UserHandle.CURRENT, customPolicy, ORIGIN_UNKNOWN, 1);
         if (!Flags.modesUi()) {
             mZenModeHelper.mConfig.manualRule = null;
         }
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/DeviceAdapterTest.java b/services/tests/vibrator/src/com/android/server/vibrator/DeviceAdapterTest.java
index d7ae046..88ed615 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/DeviceAdapterTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/DeviceAdapterTest.java
@@ -29,8 +29,10 @@
 import android.os.PersistableBundle;
 import android.os.VibrationEffect;
 import android.os.test.TestLooper;
+import android.os.vibrator.Flags;
 import android.os.vibrator.PrebakedSegment;
 import android.os.vibrator.PrimitiveSegment;
+import android.os.vibrator.PwleSegment;
 import android.os.vibrator.RampSegment;
 import android.os.vibrator.StepSegment;
 import android.os.vibrator.VibrationConfig;
@@ -57,11 +59,20 @@
     private static final int EMPTY_VIBRATOR_ID = 1;
     private static final int PWLE_VIBRATOR_ID = 2;
     private static final int PWLE_WITHOUT_FREQUENCIES_VIBRATOR_ID = 3;
+    private static final int PWLE_V2_VIBRATOR_ID = 4;
     private static final float TEST_MIN_FREQUENCY = 50;
     private static final float TEST_RESONANT_FREQUENCY = 150;
     private static final float TEST_FREQUENCY_RESOLUTION = 25;
     private static final float[] TEST_AMPLITUDE_MAP = new float[]{
             /* 50Hz= */ 0.08f, 0.16f, 0.32f, 0.64f, /* 150Hz= */ 0.8f, 0.72f, /* 200Hz= */ 0.64f};
+    private static final int TEST_MAX_ENVELOPE_EFFECT_SIZE = 10;
+    private static final int TEST_MIN_ENVELOPE_EFFECT_CONTROL_POINT_DURATION_MILLIS = 20;
+    private static final float[] TEST_FREQUENCIES_HZ = new float[]{30f, 50f, 100f, 120f, 150f};
+    private static final float[] TEST_OUTPUT_ACCELERATIONS_GS =
+            new float[]{0.3f, 0.5f, 1.0f, 0.8f, 0.6f};
+    private static final float PWLE_V2_MIN_FREQUENCY = TEST_FREQUENCIES_HZ[0];
+    private static final float PWLE_V2_MAX_FREQUENCY =
+            TEST_FREQUENCIES_HZ[TEST_FREQUENCIES_HZ.length - 1];
 
     @Rule
     public MockitoRule mMockitoRule = MockitoJUnit.rule();
@@ -296,6 +307,77 @@
         assertThat(vibration.adapt(mAdapter)).isEqualTo(expected);
     }
 
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_NORMALIZED_PWLE_EFFECTS)
+    public void testPwleSegment_withoutPwleV2Capability_returnsNull() {
+        VibrationEffect.Composed effect = new VibrationEffect.Composed(Arrays.asList(
+                new PrimitiveSegment(VibrationEffect.Composition.PRIMITIVE_SPIN, 0.5f, 100),
+                new PwleSegment(1, 0.2f, 30, 60, 20),
+                new PwleSegment(0.8f, 0.2f, 60, 100, 100),
+                new PwleSegment(0.65f, 0.65f, 100, 50, 50)),
+                /* repeatIndex= */ 1);
+
+        VibrationEffect.Composed adaptedEffect =
+                (VibrationEffect.Composed) mAdapter.adaptToVibrator(EMPTY_VIBRATOR_ID, effect);
+        assertThat(adaptedEffect).isNull();
+    }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_NORMALIZED_PWLE_EFFECTS)
+    public void testPwleSegment_withPwleV2Capability_returnsAdaptedSegments() {
+        VibrationEffect.Composed effect = new VibrationEffect.Composed(Arrays.asList(
+                new PwleSegment(1, 0.2f, 30, 60, 20),
+                new PwleSegment(0.8f, 0.2f, 60, 100, 100),
+                new PwleSegment(0.65f, 0.65f, 100, 50, 50)),
+                /* repeatIndex= */ 1);
+
+        VibrationEffect.Composed expected = new VibrationEffect.Composed(Arrays.asList(
+                new PwleSegment(1, 0.2f, 30, 60, 20),
+                new PwleSegment(0.8f, 0.2f, 60, 100, 100),
+                new PwleSegment(0.65f, 0.65f, 100, 50, 50)),
+                /* repeatIndex= */ 1);
+
+        SparseArray<VibratorController> vibrators = new SparseArray<>();
+        vibrators.put(PWLE_V2_VIBRATOR_ID, createPwleV2VibratorController(PWLE_V2_VIBRATOR_ID));
+        DeviceAdapter adapter = new DeviceAdapter(mVibrationSettings, vibrators);
+
+        assertThat(adapter.adaptToVibrator(PWLE_V2_VIBRATOR_ID, effect)).isEqualTo(expected);
+    }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_NORMALIZED_PWLE_EFFECTS)
+    public void testPwleSegment_withFrequenciesBelowSupportedRange_returnsNull() {
+        float frequencyBelowSupportedRange = PWLE_V2_MIN_FREQUENCY - 1f;
+        VibrationEffect.Composed effect = new VibrationEffect.Composed(Arrays.asList(
+                new PwleSegment(0, 0.2f, 30, 60, 20),
+                new PwleSegment(0.8f, 0.2f, 60, frequencyBelowSupportedRange, 100),
+                new PwleSegment(0.65f, 0.65f, frequencyBelowSupportedRange, 50, 50)),
+                /* repeatIndex= */ 1);
+
+        SparseArray<VibratorController> vibrators = new SparseArray<>();
+        vibrators.put(PWLE_V2_VIBRATOR_ID, createPwleV2VibratorController(PWLE_V2_VIBRATOR_ID));
+        DeviceAdapter adapter = new DeviceAdapter(mVibrationSettings, vibrators);
+
+        assertThat(adapter.adaptToVibrator(PWLE_V2_VIBRATOR_ID, effect)).isNull();
+    }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_NORMALIZED_PWLE_EFFECTS)
+    public void testPwleSegment_withFrequenciesAboveSupportedRange_returnsNull() {
+        float frequencyAboveSupportedRange = PWLE_V2_MAX_FREQUENCY + 1f;
+        VibrationEffect.Composed effect = new VibrationEffect.Composed(Arrays.asList(
+                new PwleSegment(0, 0.2f, 30, frequencyAboveSupportedRange, 20),
+                new PwleSegment(0.8f, 0.2f, frequencyAboveSupportedRange, 100, 100),
+                new PwleSegment(0.65f, 0.65f, 100, 50, 50)),
+                /* repeatIndex= */ 1);
+
+        SparseArray<VibratorController> vibrators = new SparseArray<>();
+        vibrators.put(PWLE_V2_VIBRATOR_ID, createPwleV2VibratorController(PWLE_V2_VIBRATOR_ID));
+        DeviceAdapter adapter = new DeviceAdapter(mVibrationSettings, vibrators);
+
+        assertThat(adapter.adaptToVibrator(PWLE_V2_VIBRATOR_ID, effect)).isNull();
+    }
+
     private VibratorController createEmptyVibratorController(int vibratorId) {
         return new FakeVibratorControllerProvider(mTestLooper.getLooper())
                 .newVibratorController(vibratorId, (id, vibrationId)  -> {});
@@ -318,4 +400,18 @@
         provider.setMaxAmplitudes(TEST_AMPLITUDE_MAP);
         return provider.newVibratorController(vibratorId, (id, vibrationId)  -> {});
     }
+
+    private VibratorController createPwleV2VibratorController(int vibratorId) {
+        FakeVibratorControllerProvider provider = new FakeVibratorControllerProvider(
+                mTestLooper.getLooper());
+        provider.setCapabilities(IVibrator.CAP_COMPOSE_PWLE_EFFECTS_V2);
+        provider.setResonantFrequency(TEST_RESONANT_FREQUENCY);
+        provider.setFrequenciesHz(TEST_FREQUENCIES_HZ);
+        provider.setOutputAccelerationsGs(TEST_OUTPUT_ACCELERATIONS_GS);
+        provider.setMaxEnvelopeEffectSize(TEST_MAX_ENVELOPE_EFFECT_SIZE);
+        provider.setMinEnvelopeEffectControlPointDurationMillis(
+                TEST_MIN_ENVELOPE_EFFECT_CONTROL_POINT_DURATION_MILLIS);
+
+        return provider.newVibratorController(vibratorId, (id, vibrationId)  -> {});
+    }
 }
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibrationThreadTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibrationThreadTest.java
index 58a1e84..8aa8a84 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/VibrationThreadTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/VibrationThreadTest.java
@@ -59,11 +59,13 @@
 import android.os.vibrator.Flags;
 import android.os.vibrator.PrebakedSegment;
 import android.os.vibrator.PrimitiveSegment;
+import android.os.vibrator.PwleSegment;
 import android.os.vibrator.RampSegment;
 import android.os.vibrator.StepSegment;
 import android.os.vibrator.VibrationConfig;
 import android.os.vibrator.VibrationEffectSegment;
 import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
 import android.platform.test.annotations.RequiresFlagsEnabled;
 import android.platform.test.flag.junit.CheckFlagsRule;
 import android.platform.test.flag.junit.DeviceFlagsValueProvider;
@@ -875,6 +877,49 @@
     }
 
     @Test
+    @EnableFlags(android.os.vibrator.Flags.FLAG_NORMALIZED_PWLE_EFFECTS)
+    public void vibrate_singleVibratorPwle_runsComposePwleV2() {
+        FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(VIBRATOR_ID);
+        fakeVibrator.setCapabilities(IVibrator.CAP_COMPOSE_PWLE_EFFECTS_V2);
+        fakeVibrator.setResonantFrequency(150);
+        fakeVibrator.setFrequenciesHz(new float[]{30f, 50f, 100f, 120f, 150f});
+        fakeVibrator.setOutputAccelerationsGs(new float[]{0.3f, 0.5f, 1.0f, 0.8f, 0.6f});
+        fakeVibrator.setMaxEnvelopeEffectSize(10);
+        fakeVibrator.setMinEnvelopeEffectControlPointDurationMillis(20);
+
+        VibrationEffect effect = VibrationEffect.startWaveformEnvelope()
+                .addControlPoint(/*amplitude=*/ 0.0f, /*frequencyHz=*/ 60f, /*timeMillis=*/ 20)
+                .addControlPoint(/*amplitude=*/ 0.3f, /*frequencyHz=*/ 100f, /*timeMillis=*/ 30)
+                .addControlPoint(/*amplitude=*/ 0.4f, /*frequencyHz=*/ 120f, /*timeMillis=*/ 20)
+                .addControlPoint(/*amplitude=*/ 0.0f, /*frequencyHz=*/ 120f, /*timeMillis=*/ 30)
+                .build();
+        HalVibration vibration = startThreadAndDispatcher(effect);
+        waitForCompletion();
+
+        verify(mManagerHooks).noteVibratorOn(eq(UID), eq(100L));
+        verify(mManagerHooks).noteVibratorOff(eq(UID));
+        verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibration.id));
+        verifyCallbacksTriggered(vibration, Status.FINISHED);
+        assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
+        assertEquals(Arrays.asList(
+                        expectedPwle(/* startAmplitude= */ 0.0f, /* endAmplitude= */ 0.0f,
+                                /* startFrequencyHz= */ 60f, /* endFrequencyHz= */ 60f,
+                                /* duration= */ 20),
+                        expectedPwle(/* startAmplitude= */ 0.0f, /* endAmplitude= */ 0.3f,
+                                /* startFrequencyHz= */ 60f, /* endFrequencyHz= */ 100f,
+                                /* duration= */ 30),
+                        expectedPwle(/* startAmplitude= */ 0.3f, /* endAmplitude= */ 0.4f,
+                                /* startFrequencyHz= */ 100f, /* endFrequencyHz= */ 120f,
+                                /* duration= */ 20),
+                        expectedPwle(/* startAmplitude= */ 0.4f, /* endAmplitude= */ 0.0f,
+                                /* startFrequencyHz= */ 120f, /* endFrequencyHz= */ 120f,
+                                /* duration= */ 30)
+                ),
+                fakeVibrator.getEffectSegments(vibration.id));
+
+    }
+
+    @Test
     @DisableFlags(android.os.vibrator.Flags.FLAG_NORMALIZED_PWLE_EFFECTS)
     public void vibrate_singleVibratorPwle_runsComposePwle() {
         FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(VIBRATOR_ID);
@@ -1968,6 +2013,12 @@
                 duration);
     }
 
+    private VibrationEffectSegment expectedPwle(float startAmplitude, float endAmplitude,
+            float startFrequencyHz, float endFrequencyHz, int duration) {
+        return new PwleSegment(startAmplitude, endAmplitude, startFrequencyHz, endFrequencyHz,
+                duration);
+    }
+
     private List<Float> expectedAmplitudes(int... amplitudes) {
         return Arrays.stream(amplitudes)
                 .mapToObj(amplitude -> amplitude / 255f)
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibratorControllerTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibratorControllerTest.java
index 8179369..bc8db3b 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/VibratorControllerTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/VibratorControllerTest.java
@@ -44,6 +44,7 @@
 import android.os.test.TestLooper;
 import android.os.vibrator.PrebakedSegment;
 import android.os.vibrator.PrimitiveSegment;
+import android.os.vibrator.PwleSegment;
 import android.os.vibrator.RampSegment;
 
 import androidx.test.InstrumentationRegistry;
@@ -268,6 +269,22 @@
     }
 
     @Test
+    public void on_withComposedPwleV2_performsEffect() {
+        mockVibratorCapabilities(IVibrator.CAP_COMPOSE_PWLE_EFFECTS_V2);
+        when(mNativeWrapperMock.composePwleV2(any(), anyLong())).thenReturn(15L);
+        VibratorController controller = createController();
+
+        PwleSegment[] primitives = new PwleSegment[]{
+                new PwleSegment(/* startAmplitude= */ 0, /* endAmplitude= */ 1,
+                        /* startFrequencyHz= */ 100, /* endFrequencyHz= */ 200, /* duration= */ 10)
+        };
+        assertEquals(15L, controller.on(primitives, 12));
+        assertTrue(controller.isVibrating());
+
+        verify(mNativeWrapperMock).composePwleV2(eq(primitives), eq(12L));
+    }
+
+    @Test
     public void off_turnsOffVibrator() {
         when(mNativeWrapperMock.on(anyLong(), anyLong())).thenAnswer(args -> args.getArgument(0));
         VibratorController controller = createController();
diff --git a/services/tests/vibrator/utils/com/android/server/vibrator/FakeVibratorControllerProvider.java b/services/tests/vibrator/utils/com/android/server/vibrator/FakeVibratorControllerProvider.java
index 75a9cedf..2c3e9b2 100644
--- a/services/tests/vibrator/utils/com/android/server/vibrator/FakeVibratorControllerProvider.java
+++ b/services/tests/vibrator/utils/com/android/server/vibrator/FakeVibratorControllerProvider.java
@@ -26,6 +26,7 @@
 import android.os.VibratorInfo;
 import android.os.vibrator.PrebakedSegment;
 import android.os.vibrator.PrimitiveSegment;
+import android.os.vibrator.PwleSegment;
 import android.os.vibrator.RampSegment;
 import android.os.vibrator.StepSegment;
 import android.os.vibrator.VibrationEffectSegment;
@@ -194,6 +195,19 @@
         }
 
         @Override
+        public long composePwleV2(PwleSegment[] primitives, long vibrationId) {
+            long duration = 0;
+            for (PwleSegment primitive: primitives) {
+                duration += primitive.getDuration();
+                recordEffectSegment(vibrationId, primitive);
+            }
+            applyLatency(mOnLatency);
+            scheduleListener(duration, vibrationId);
+
+            return duration;
+        }
+
+        @Override
         public void setExternalControl(boolean enabled) {
             mExternalControlStates.add(enabled);
         }
diff --git a/services/tests/wmtests/src/com/android/server/policy/KeyGestureEventTests.java b/services/tests/wmtests/src/com/android/server/policy/KeyGestureEventTests.java
index d83ffd1..ad11c26 100644
--- a/services/tests/wmtests/src/com/android/server/policy/KeyGestureEventTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/KeyGestureEventTests.java
@@ -26,6 +26,7 @@
 import static com.android.server.policy.PhoneWindowManager.POWER_VOLUME_UP_BEHAVIOR_MUTE;
 import static com.android.server.policy.PhoneWindowManager.SETTINGS_KEY_BEHAVIOR_NOTIFICATION_PANEL;
 
+import android.hardware.input.InputSettings;
 import android.hardware.input.KeyGestureEvent;
 import android.os.RemoteException;
 import android.platform.test.annotations.DisableFlags;
@@ -405,6 +406,36 @@
                 META_ON | ALT_ON);
     }
 
+    @Test
+    @EnableFlags({com.android.hardware.input.Flags.FLAG_KEYBOARD_A11Y_SHORTCUT_CONTROL,
+            com.android.hardware.input.Flags.FLAG_KEYBOARD_A11Y_BOUNCE_KEYS_FLAG,
+            com.android.hardware.input.Flags.FLAG_KEYBOARD_A11Y_SLOW_KEYS_FLAG,
+            com.android.hardware.input.Flags.FLAG_KEYBOARD_A11Y_STICKY_KEYS_FLAG,
+            com.android.hardware.input.Flags.FLAG_KEYBOARD_A11Y_MOUSE_KEYS})
+    @DisableFlags(com.android.hardware.input.Flags.FLAG_USE_KEY_GESTURE_EVENT_HANDLER)
+    public void testKeyboardAccessibilityToggleShortcutPress() {
+        testShortcutInternal("Meta + Alt + 3 -> Toggle Bounce Keys",
+                new int[]{META_KEY, ALT_KEY, KeyEvent.KEYCODE_3},
+                KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_BOUNCE_KEYS,
+                KeyEvent.KEYCODE_3,
+                META_ON | ALT_ON);
+        testShortcutInternal("Meta + Alt + 4 -> Toggle Mouse Keys",
+                new int[]{META_KEY, ALT_KEY, KeyEvent.KEYCODE_4},
+                KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_MOUSE_KEYS,
+                KeyEvent.KEYCODE_4,
+                META_ON | ALT_ON);
+        testShortcutInternal("Meta + Alt + 5 -> Toggle Sticky Keys",
+                new int[]{META_KEY, ALT_KEY, KeyEvent.KEYCODE_5},
+                KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_STICKY_KEYS,
+                KeyEvent.KEYCODE_5,
+                META_ON | ALT_ON);
+        testShortcutInternal("Meta + Alt + 6 -> Toggle Slow Keys",
+                new int[]{META_KEY, ALT_KEY, KeyEvent.KEYCODE_6},
+                KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_SLOW_KEYS,
+                KeyEvent.KEYCODE_6,
+                META_ON | ALT_ON);
+    }
+
     private void testShortcutInternal(String testName, int[] testKeys,
             @KeyGestureEvent.KeyGestureType int expectedKeyGestureType, int expectedKey,
             int expectedModifierState) {
@@ -723,4 +754,56 @@
                 sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_TALKBACK));
         mPhoneWindowManager.assertTalkBack(false);
     }
+
+    @Test
+    @EnableFlags({com.android.hardware.input.Flags.FLAG_KEYBOARD_A11Y_SHORTCUT_CONTROL,
+            com.android.hardware.input.Flags.FLAG_KEYBOARD_A11Y_STICKY_KEYS_FLAG})
+    public void testKeyGestureToggleStickyKeys() {
+        Assert.assertTrue(
+                sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_STICKY_KEYS));
+        Assert.assertTrue(InputSettings.isAccessibilityStickyKeysEnabled(mContext));
+
+        Assert.assertTrue(
+                sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_STICKY_KEYS));
+        Assert.assertFalse(InputSettings.isAccessibilityStickyKeysEnabled(mContext));
+    }
+
+    @Test
+    @EnableFlags({com.android.hardware.input.Flags.FLAG_KEYBOARD_A11Y_SHORTCUT_CONTROL,
+            com.android.hardware.input.Flags.FLAG_KEYBOARD_A11Y_SLOW_KEYS_FLAG})
+    public void testKeyGestureToggleSlowKeys() {
+        Assert.assertTrue(
+                sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_SLOW_KEYS));
+        Assert.assertTrue(InputSettings.isAccessibilitySlowKeysEnabled(mContext));
+
+        Assert.assertTrue(
+                sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_SLOW_KEYS));
+        Assert.assertFalse(InputSettings.isAccessibilitySlowKeysEnabled(mContext));
+    }
+
+    @Test
+    @EnableFlags({com.android.hardware.input.Flags.FLAG_KEYBOARD_A11Y_SHORTCUT_CONTROL,
+            com.android.hardware.input.Flags.FLAG_KEYBOARD_A11Y_MOUSE_KEYS})
+    public void testKeyGestureToggleMouseKeys() {
+        Assert.assertTrue(
+                sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_MOUSE_KEYS));
+        Assert.assertTrue(InputSettings.isAccessibilityMouseKeysEnabled(mContext));
+
+        Assert.assertTrue(
+                sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_MOUSE_KEYS));
+        Assert.assertFalse(InputSettings.isAccessibilityMouseKeysEnabled(mContext));
+    }
+
+    @Test
+    @EnableFlags({com.android.hardware.input.Flags.FLAG_KEYBOARD_A11Y_SHORTCUT_CONTROL,
+            com.android.hardware.input.Flags.FLAG_KEYBOARD_A11Y_BOUNCE_KEYS_FLAG})
+    public void testKeyGestureToggleBounceKeys() {
+        Assert.assertTrue(
+                sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_BOUNCE_KEYS));
+        Assert.assertTrue(InputSettings.isAccessibilityBounceKeysEnabled(mContext));
+
+        Assert.assertTrue(
+                sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_BOUNCE_KEYS));
+        Assert.assertFalse(InputSettings.isAccessibilityBounceKeysEnabled(mContext));
+    }
 }
diff --git a/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutTests.java b/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutTests.java
index c186a03..28ae271 100644
--- a/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutTests.java
@@ -43,6 +43,8 @@
 import android.app.role.RoleManager;
 import android.content.ComponentName;
 import android.content.Intent;
+import android.hardware.input.AppLaunchData;
+import android.hardware.input.KeyGestureEvent;
 import android.os.RemoteException;
 import android.platform.test.annotations.DisableFlags;
 import android.platform.test.annotations.EnableFlags;
@@ -85,7 +87,8 @@
      * Test meta+ shortcuts defined in bookmarks.xml.
      */
     @Test
-    public void testMetaShortcuts() {
+    @DisableFlags(com.android.hardware.input.Flags.FLAG_USE_KEY_GESTURE_EVENT_HANDLER)
+    public void testMetaShortcuts_withoutKeyGestureEventHandling() {
         for (int i = 0; i < INTENT_SHORTCUTS.size(); i++) {
             final int keyCode = INTENT_SHORTCUTS.keyAt(i);
             final String category = INTENT_SHORTCUTS.valueAt(i);
@@ -115,6 +118,49 @@
 
     }
 
+    @Test
+    @EnableFlags(com.android.hardware.input.Flags.FLAG_USE_KEY_GESTURE_EVENT_HANDLER)
+    public void testMetaShortcuts_withKeyGestureEventHandling() {
+        for (int i = 0; i < INTENT_SHORTCUTS.size(); i++) {
+            final String category = INTENT_SHORTCUTS.valueAt(i);
+            mPhoneWindowManager.sendKeyGestureEvent(
+                    new KeyGestureEvent.Builder()
+                            .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION)
+                            .setAction(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+                            .setAppLaunchData(AppLaunchData.createLaunchDataForCategory(category))
+                            .build()
+            );
+            mPhoneWindowManager.assertLaunchCategory(category);
+        }
+
+        mPhoneWindowManager.overrideRoleManager();
+        for (int i = 0; i < ROLE_SHORTCUTS.size(); i++) {
+            final String role = ROLE_SHORTCUTS.valueAt(i);
+
+            mPhoneWindowManager.sendKeyGestureEvent(
+                    new KeyGestureEvent.Builder()
+                            .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION)
+                            .setAction(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+                            .setAppLaunchData(AppLaunchData.createLaunchDataForRole(role))
+                            .build()
+            );
+            mPhoneWindowManager.assertLaunchRole(role);
+        }
+
+        mPhoneWindowManager.sendKeyGestureEvent(
+                new KeyGestureEvent.Builder()
+                        .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION)
+                        .setAction(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+                        .setAppLaunchData(
+                                new AppLaunchData.ComponentData("com.test",
+                                        "com.test.BookmarkTest"))
+                        .build()
+        );
+        mPhoneWindowManager.assertActivityTargetLaunched(
+                new ComponentName("com.test", "com.test.BookmarkTest"));
+
+    }
+
     /**
      * ALT + TAB to show recent apps.
      */
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
index b25b13f..94a4002 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
@@ -345,7 +345,8 @@
                     .setResultTo(resultTo)
                     .setRequestCode(requestCode)
                     .setReason("testLaunchActivityPermissionDenied")
-                    .setActivityOptions(new SafeActivityOptions(options))
+                    .setActivityOptions(new SafeActivityOptions(
+                            options, Binder.getCallingPid(), Binder.getCallingUid()))
                     .execute();
             verify(options, times(1)).abort();
         }
@@ -469,7 +470,8 @@
         optionStarter
                 .setReason("testCreateTaskLayout")
                 .setActivityInfo(info)
-                .setActivityOptions(new SafeActivityOptions(options))
+                .setActivityOptions(new SafeActivityOptions(
+                        options, Binder.getCallingPid(), Binder.getCallingUid()))
                 .execute();
 
         // verify that values are passed to the modifier. Values are passed thrice -- two for
@@ -775,7 +777,8 @@
                 .setCaller(caller)
                 .setCallingUid(UNIMPORTANT_UID)
                 .setRealCallingUid(UNIMPORTANT_UID2)
-                .setActivityOptions(new SafeActivityOptions(options))
+                .setActivityOptions(new SafeActivityOptions(
+                        options, Binder.getCallingPid(), Binder.getCallingUid()))
                 .setOutActivity(outActivity);
 
         final int result = starter.setReason("testPinnedSingleInstanceAborted").execute();
@@ -811,7 +814,8 @@
         prepareStarter(FLAG_ACTIVITY_NEW_TASK, false /* mockGetRootTask */)
                 .setReason("testAdjustLaunchTargetWithAdjacentTask")
                 .setIntent(activity.intent)
-                .setActivityOptions(options.toBundle())
+                .setActivityOptions(options.toBundle(),
+                        Binder.getCallingPid(), Binder.getCallingUid())
                 .execute();
 
         // Verify the activity will be launched into the original parent
@@ -883,7 +887,8 @@
                 .setLaunchDisplayId(secondaryDisplay.mDisplayId);
         final int result = starter.setReason("testDeliverIntentToTopActivityOfNonTopDisplay")
                 .setIntent(topActivityOnSecondaryDisplay.intent)
-                .setActivityOptions(options.toBundle())
+                .setActivityOptions(options.toBundle(),
+                        Binder.getCallingPid(), Binder.getCallingUid())
                 .execute();
 
         // Ensure result is delivering intent to top.
@@ -928,7 +933,8 @@
                 .setLaunchDisplayId(secondaryDisplay.mDisplayId);
         final int result = starter.setReason("testBringTaskToFrontOnSecondaryDisplay")
                 .setIntent(singleTaskActivity.intent)
-                .setActivityOptions(options.toBundle())
+                .setActivityOptions(options.toBundle(),
+                        Binder.getCallingPid(), Binder.getCallingUid())
                 .execute();
 
         // Ensure result is moving existing task to front.
@@ -974,7 +980,8 @@
                 .setLaunchDisplayId(secondaryDisplay.mDisplayId);
         final int result = starter.setReason("testStartActivityOnVirtualDisplay")
                 .setIntent(topActivityOnSecondaryDisplay.intent)
-                .setActivityOptions(options.toBundle())
+                .setActivityOptions(options.toBundle(),
+                        Binder.getCallingPid(), Binder.getCallingUid())
                 .execute();
 
         // Ensure result is delivering intent to top.
@@ -1017,7 +1024,8 @@
                 .setLaunchDisplayId(secondaryDisplay.mDisplayId);
         final int result = starter.setReason("testStartOptedOutActivityOnVirtualDisplay")
                 .setIntent(topActivityOnSecondaryDisplay.intent)
-                .setActivityOptions(options.toBundle())
+                .setActivityOptions(options.toBundle(),
+                        Binder.getCallingPid(), Binder.getCallingUid())
                 .execute();
 
         // Ensure result is canceled.
@@ -1099,7 +1107,8 @@
                 .setLaunchDisplayId(secondaryDisplay.mDisplayId);
         starter.setReason("testReparentTopFocusedActivityToSecondaryDisplay")
                 .setIntent(topActivity.intent)
-                .setActivityOptions(options.toBundle())
+                .setActivityOptions(options.toBundle(),
+                        Binder.getCallingPid(), Binder.getCallingUid())
                 .execute();
 
         // Ensure the activity is moved to secondary display.
@@ -1121,7 +1130,8 @@
         options.setFreezeRecentTasksReordering();
 
         starter.setReason("testFreezeTaskListActivityOption")
-                .setActivityOptions(new SafeActivityOptions(options))
+                .setActivityOptions(new SafeActivityOptions(options,
+                        Binder.getCallingPid(), Binder.getCallingUid()))
                 .execute();
 
         verify(recentTasks, times(1)).setFreezeTaskListReordering();
@@ -1143,7 +1153,8 @@
         options.setFreezeRecentTasksReordering();
 
         starter.setReason("testFreezeTaskListActivityOptionFailedStart")
-                .setActivityOptions(new SafeActivityOptions(options))
+                .setActivityOptions(new SafeActivityOptions(options,
+                        Binder.getCallingPid(), Binder.getCallingUid()))
                 .execute();
 
         // Simulate a failed start
@@ -1217,7 +1228,7 @@
 
     @Test
     public void testRecycleTaskWakeUpWhenDreaming() {
-        doNothing().when(mWm.mAtmService.mTaskSupervisor).wakeUp(anyString());
+        doNothing().when(mWm.mAtmService.mTaskSupervisor).wakeUp(anyInt(), anyString());
         doReturn(true).when(mWm.mAtmService).isDreaming();
         final ActivityStarter starter = prepareStarter(0 /* flags */);
         final ActivityRecord target = new ActivityBuilder(mAtm).setCreateTask(true).build();
@@ -1232,7 +1243,7 @@
         assertTrue(target.currentLaunchCanTurnScreenOn());
         // In real case, dream activity has a higher priority (TaskDisplayArea#getPriority) that
         // will be put at a higher z-order. So it relies on wakeUp() to be dismissed.
-        verify(mWm.mAtmService.mTaskSupervisor).wakeUp(anyString());
+        verify(mWm.mAtmService.mTaskSupervisor).wakeUp(eq(target.getDisplayId()), anyString());
     }
 
     @Test
@@ -1244,7 +1255,8 @@
         final ActivityRecord[] outActivity = new ActivityRecord[1];
 
         // Activity must not land on split-screen task if currently not in split-screen mode.
-        starter.setActivityOptions(options.toBundle())
+        starter.setActivityOptions(options.toBundle(),
+                        Binder.getCallingPid(), Binder.getCallingUid())
                 .setReason("testTargetTaskInSplitScreen")
                 .setOutActivity(outActivity).execute();
         assertThat(outActivity[0].inMultiWindowMode()).isFalse();
@@ -1269,7 +1281,8 @@
         final ActivityRecord[] outActivity = new ActivityRecord[1];
 
         // Activity must not land on split-screen task if currently not in split-screen mode.
-        starter.setActivityOptions(options.toBundle())
+        starter.setActivityOptions(options.toBundle(),
+                        Binder.getCallingPid(), Binder.getCallingUid())
                 .setReason("testLaunchAdjacentDisabled")
                 .setOutActivity(outActivity).execute();
         assertThat(outActivity[0].inMultiWindowMode()).isFalse();
@@ -1297,7 +1310,8 @@
         doReturn(true).when(keyguard).isKeyguardOccluded(anyInt());
         registerTestTransitionPlayer();
         starter.setReason("testTransientLaunchWithKeyguard")
-                .setActivityOptions(ActivityOptions.makeBasic().setTransientLaunch().toBundle())
+                .setActivityOptions(ActivityOptions.makeBasic().setTransientLaunch().toBundle(),
+                        Binder.getCallingPid(), Binder.getCallingUid())
                 .setIntent(target.intent)
                 .execute();
         final TransitionController controller = mRootWindowContainer.mTransitionController;
@@ -1474,7 +1488,8 @@
         intent.setComponent(ActivityBuilder.getDefaultComponent());
         starter.setReason("testLaunchCookie_newTask")
                 .setIntent(intent)
-                .setActivityOptions(options.toBundle())
+                .setActivityOptions(options.toBundle(),
+                        Binder.getCallingPid(), Binder.getCallingUid())
                 .execute();
 
         // Verify the cookie is set
@@ -1486,7 +1501,8 @@
         newOptions.setLaunchCookie(newCookie);
         starter.setReason("testLaunchCookie_existingTask")
                 .setIntent(intent)
-                .setActivityOptions(newOptions.toBundle())
+                .setActivityOptions(newOptions.toBundle(),
+                        Binder.getCallingPid(), Binder.getCallingUid())
                 .execute();
 
         // Verify the cookie is updated
@@ -1512,7 +1528,8 @@
         final ActivityOptions options = ActivityOptions.makeRemoteAnimation(adaptor);
         starter.setReason("testRemoteAnimation_existingTask")
                 .setIntent(intent)
-                .setActivityOptions(options.toBundle())
+                .setActivityOptions(options.toBundle(),
+                        Binder.getCallingPid(), Binder.getCallingUid())
                 .execute();
 
         // Verify the remote animation is updated.
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskSupervisorTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskSupervisorTests.java
index 27a4a2b..7f260f8 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskSupervisorTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskSupervisorTests.java
@@ -376,7 +376,8 @@
         IBinder launchCookie = new Binder("test_launch_cookie");
         ActivityOptions options = ActivityOptions.makeBasic();
         options.setLaunchCookie(launchCookie);
-        SafeActivityOptions safeOptions = SafeActivityOptions.fromBundle(options.toBundle());
+        SafeActivityOptions safeOptions = SafeActivityOptions.fromBundle(options.toBundle(),
+                Binder.getCallingPid(), Binder.getCallingUid());
 
         doNothing().when(mSupervisor.mService).moveTaskToFrontLocked(eq(null), eq(null), anyInt(),
                 anyInt(), any());
@@ -393,7 +394,8 @@
         final ActivityRecord activity = new ActivityBuilder(mAtm).setCreateTask(true).build();
 
         SafeActivityOptions safeOptions = SafeActivityOptions.fromBundle(
-                ActivityOptions.makeBasic().toBundle());
+                ActivityOptions.makeBasic().toBundle(),
+                Binder.getCallingPid(), Binder.getCallingUid());
 
         doNothing().when(mSupervisor.mService).moveTaskToFrontLocked(eq(null), eq(null), anyInt(),
                 anyInt(), any());
diff --git a/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerExemptionTests.java b/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerExemptionTests.java
index 7509681..965b65c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerExemptionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerExemptionTests.java
@@ -39,7 +39,6 @@
 
 import android.app.ActivityOptions;
 import android.app.AppOpsManager;
-import android.app.BackgroundStartPrivileges;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -223,12 +222,12 @@
 
         // prepare call
         PendingIntentRecord originatingPendingIntent = mPendingIntentRecord;
-        BackgroundStartPrivileges forcedBalByPiSender = BackgroundStartPrivileges.NONE;
+        boolean allowBalExemptionForSystemProcess = false;
         Intent intent = TEST_INTENT;
         ActivityOptions checkedOptions = mCheckedOptions;
         BackgroundActivityStartController.BalState balState = mController.new BalState(callingUid,
                 callingPid, callingPackage, realCallingUid, realCallingPid, mCallerApp,
-                originatingPendingIntent, forcedBalByPiSender, mResultRecord, intent,
+                originatingPendingIntent, allowBalExemptionForSystemProcess, mResultRecord, intent,
                 checkedOptions);
 
         assertWithMessage(balState.toString()).that(balState.isPendingIntent()).isTrue();
@@ -263,12 +262,12 @@
 
         // prepare call
         PendingIntentRecord originatingPendingIntent = mPendingIntentRecord;
-        BackgroundStartPrivileges forcedBalByPiSender = BackgroundStartPrivileges.NONE;
+        boolean allowBalExemptionForSystemProcess = false;
         Intent intent = TEST_INTENT;
         ActivityOptions checkedOptions = mCheckedOptions;
         BackgroundActivityStartController.BalState balState = mController.new BalState(callingUid,
                 callingPid, callingPackage, realCallingUid, realCallingPid, mCallerApp,
-                originatingPendingIntent, forcedBalByPiSender, mResultRecord, intent,
+                originatingPendingIntent, allowBalExemptionForSystemProcess, mResultRecord, intent,
                 checkedOptions);
 
         // call
@@ -295,12 +294,12 @@
 
         // prepare call
         PendingIntentRecord originatingPendingIntent = mPendingIntentRecord;
-        BackgroundStartPrivileges forcedBalByPiSender = BackgroundStartPrivileges.NONE;
+        boolean allowBalExemptionForSystemProcess = false;
         Intent intent = TEST_INTENT;
         ActivityOptions checkedOptions = mCheckedOptions;
         BackgroundActivityStartController.BalState balState = mController.new BalState(callingUid,
                 callingPid, callingPackage, realCallingUid, realCallingPid, mCallerApp,
-                originatingPendingIntent, forcedBalByPiSender, mResultRecord, intent,
+                originatingPendingIntent, allowBalExemptionForSystemProcess, mResultRecord, intent,
                 checkedOptions);
 
         // call
@@ -328,14 +327,14 @@
 
         // prepare call
         PendingIntentRecord originatingPendingIntent = mPendingIntentRecord;
-        BackgroundStartPrivileges forcedBalByPiSender = BackgroundStartPrivileges.NONE;
+        boolean allowBalExemptionForSystemProcess = false;
         Intent intent = TEST_INTENT;
         ActivityOptions checkedOptions = mCheckedOptions
                 .setPendingIntentCreatorBackgroundActivityStartMode(
                         MODE_BACKGROUND_ACTIVITY_START_ALLOW_IF_VISIBLE);
         BackgroundActivityStartController.BalState balState = mController.new BalState(callingUid,
                 callingPid, callingPackage, realCallingUid, realCallingPid, mCallerApp,
-                originatingPendingIntent, forcedBalByPiSender, mResultRecord, intent,
+                originatingPendingIntent, allowBalExemptionForSystemProcess, mResultRecord, intent,
                 checkedOptions);
 
         // call
@@ -363,14 +362,14 @@
 
         // prepare call
         PendingIntentRecord originatingPendingIntent = mPendingIntentRecord;
-        BackgroundStartPrivileges forcedBalByPiSender = BackgroundStartPrivileges.NONE;
+        boolean allowBalExemptionForSystemProcess = false;
         Intent intent = TEST_INTENT;
         ActivityOptions checkedOptions = mCheckedOptions
                 .setPendingIntentCreatorBackgroundActivityStartMode(
                         MODE_BACKGROUND_ACTIVITY_START_ALLOW_IF_VISIBLE);
         BackgroundActivityStartController.BalState balState = mController.new BalState(callingUid,
                 callingPid, callingPackage, realCallingUid, realCallingPid, mCallerApp,
-                originatingPendingIntent, forcedBalByPiSender, mResultRecord, intent,
+                originatingPendingIntent, allowBalExemptionForSystemProcess, mResultRecord, intent,
                 checkedOptions);
 
         // call
@@ -405,12 +404,12 @@
 
         // prepare call
         PendingIntentRecord originatingPendingIntent = mPendingIntentRecord;
-        BackgroundStartPrivileges forcedBalByPiSender = BackgroundStartPrivileges.NONE;
+        boolean allowBalExemptionForSystemProcess = false;
         Intent intent = TEST_INTENT;
         ActivityOptions checkedOptions = mCheckedOptions;
         BackgroundActivityStartController.BalState balState = mController.new BalState(callingUid,
                 callingPid, callingPackage, realCallingUid, realCallingPid, mCallerApp,
-                originatingPendingIntent, forcedBalByPiSender, mResultRecord, intent,
+                originatingPendingIntent, allowBalExemptionForSystemProcess, mResultRecord, intent,
                 checkedOptions);
 
         // call
@@ -441,12 +440,12 @@
 
         // prepare call
         PendingIntentRecord originatingPendingIntent = mPendingIntentRecord;
-        BackgroundStartPrivileges forcedBalByPiSender = BackgroundStartPrivileges.NONE;
+        boolean allowBalExemptionForSystemProcess = false;
         Intent intent = TEST_INTENT;
         ActivityOptions checkedOptions = mCheckedOptions;
         BackgroundActivityStartController.BalState balState = mController.new BalState(callingUid,
                 callingPid, callingPackage, realCallingUid, realCallingPid, null,
-                originatingPendingIntent, forcedBalByPiSender, mResultRecord, intent,
+                originatingPendingIntent, allowBalExemptionForSystemProcess, mResultRecord, intent,
                 checkedOptions);
 
         // call
@@ -490,12 +489,12 @@
 
         // prepare call
         PendingIntentRecord originatingPendingIntent = mPendingIntentRecord;
-        BackgroundStartPrivileges forcedBalByPiSender = BackgroundStartPrivileges.NONE;
+        boolean allowBalExemptionForSystemProcess = false;
         Intent intent = TEST_INTENT;
         ActivityOptions checkedOptions = mCheckedOptions;
         BackgroundActivityStartController.BalState balState = mController.new BalState(callingUid,
                 callingPid, callingPackage, realCallingUid, realCallingPid, null,
-                originatingPendingIntent, forcedBalByPiSender, mResultRecord, intent,
+                originatingPendingIntent, allowBalExemptionForSystemProcess, mResultRecord, intent,
                 checkedOptions);
 
         // call
@@ -526,12 +525,12 @@
 
         // prepare call
         PendingIntentRecord originatingPendingIntent = mPendingIntentRecord;
-        BackgroundStartPrivileges forcedBalByPiSender = BackgroundStartPrivileges.NONE;
+        boolean allowBalExemptionForSystemProcess = false;
         Intent intent = TEST_INTENT;
         ActivityOptions checkedOptions = mCheckedOptions;
         BackgroundActivityStartController.BalState balState = mController.new BalState(callingUid,
                 callingPid, callingPackage, realCallingUid, realCallingPid, null,
-                originatingPendingIntent, forcedBalByPiSender, mResultRecord, intent,
+                originatingPendingIntent, allowBalExemptionForSystemProcess, mResultRecord, intent,
                 checkedOptions);
 
         // call
@@ -563,14 +562,14 @@
 
         // prepare call
         PendingIntentRecord originatingPendingIntent = mPendingIntentRecord;
-        BackgroundStartPrivileges forcedBalByPiSender = BackgroundStartPrivileges.NONE;
+        boolean allowBalExemptionForSystemProcess = false;
         Intent intent = TEST_INTENT;
         ActivityOptions checkedOptions = mCheckedOptions
                 .setPendingIntentBackgroundActivityStartMode(
                         MODE_BACKGROUND_ACTIVITY_START_ALLOW_IF_VISIBLE);
         BackgroundActivityStartController.BalState balState = mController.new BalState(callingUid,
                 callingPid, callingPackage, realCallingUid, realCallingPid, null,
-                originatingPendingIntent, forcedBalByPiSender, mResultRecord, intent,
+                originatingPendingIntent, allowBalExemptionForSystemProcess, mResultRecord, intent,
                 checkedOptions);
 
         // call
@@ -597,12 +596,12 @@
 
         // prepare call
         PendingIntentRecord originatingPendingIntent = mPendingIntentRecord;
-        BackgroundStartPrivileges forcedBalByPiSender = BackgroundStartPrivileges.NONE;
+        boolean allowBalExemptionForSystemProcess = false;
         Intent intent = TEST_INTENT;
         ActivityOptions checkedOptions = mCheckedOptions;
         BackgroundActivityStartController.BalState balState = mController.new BalState(callingUid,
                 callingPid, callingPackage, realCallingUid, realCallingPid, null,
-                originatingPendingIntent, forcedBalByPiSender, mResultRecord, intent,
+                originatingPendingIntent, allowBalExemptionForSystemProcess, mResultRecord, intent,
                 checkedOptions);
 
         // call
@@ -630,14 +629,14 @@
 
         // prepare call
         PendingIntentRecord originatingPendingIntent = mPendingIntentRecord;
-        BackgroundStartPrivileges forcedBalByPiSender = BackgroundStartPrivileges.NONE;
+        boolean allowBalExemptionForSystemProcess = false;
         Intent intent = TEST_INTENT;
         ActivityOptions checkedOptions = mCheckedOptions;
         checkedOptions.setPendingIntentBackgroundActivityStartMode(
                 MODE_BACKGROUND_ACTIVITY_START_ALLOW_ALWAYS);
         BackgroundActivityStartController.BalState balState = mController.new BalState(callingUid,
                 callingPid, callingPackage, realCallingUid, realCallingPid, null,
-                originatingPendingIntent, forcedBalByPiSender, mResultRecord, intent,
+                originatingPendingIntent, allowBalExemptionForSystemProcess, mResultRecord, intent,
                 checkedOptions);
 
         // call
@@ -664,12 +663,12 @@
 
         // prepare call
         PendingIntentRecord originatingPendingIntent = mPendingIntentRecord;
-        BackgroundStartPrivileges forcedBalByPiSender = BackgroundStartPrivileges.NONE;
+        boolean allowBalExemptionForSystemProcess = false;
         Intent intent = TEST_INTENT;
         ActivityOptions checkedOptions = mCheckedOptions;
         BackgroundActivityStartController.BalState balState = mController.new BalState(callingUid,
                 callingPid, callingPackage, realCallingUid, realCallingPid, null,
-                originatingPendingIntent, forcedBalByPiSender, mResultRecord, intent,
+                originatingPendingIntent, allowBalExemptionForSystemProcess, mResultRecord, intent,
                 checkedOptions);
 
         // call
@@ -697,14 +696,14 @@
 
         // prepare call
         PendingIntentRecord originatingPendingIntent = mPendingIntentRecord;
-        BackgroundStartPrivileges forcedBalByPiSender = BackgroundStartPrivileges.NONE;
+        boolean allowBalExemptionForSystemProcess = false;
         Intent intent = TEST_INTENT;
         ActivityOptions checkedOptions =
                 mCheckedOptions.setPendingIntentBackgroundActivityStartMode(
                         MODE_BACKGROUND_ACTIVITY_START_ALLOW_ALWAYS);
         BackgroundActivityStartController.BalState balState = mController.new BalState(callingUid,
                 callingPid, callingPackage, realCallingUid, realCallingPid, null,
-                originatingPendingIntent, forcedBalByPiSender, mResultRecord, intent,
+                originatingPendingIntent, allowBalExemptionForSystemProcess, mResultRecord, intent,
                 checkedOptions);
 
         // call
@@ -733,12 +732,12 @@
 
         // prepare call
         PendingIntentRecord originatingPendingIntent = mPendingIntentRecord;
-        BackgroundStartPrivileges forcedBalByPiSender = BackgroundStartPrivileges.NONE;
+        boolean allowBalExemptionForSystemProcess = false;
         Intent intent = TEST_INTENT;
         ActivityOptions checkedOptions = mCheckedOptions;
         BackgroundActivityStartController.BalState balState = mController.new BalState(callingUid,
                 callingPid, callingPackage, realCallingUid, realCallingPid, null,
-                originatingPendingIntent, forcedBalByPiSender, mResultRecord, intent,
+                originatingPendingIntent, allowBalExemptionForSystemProcess, mResultRecord, intent,
                 checkedOptions);
 
         // call
@@ -764,12 +763,12 @@
 
         // prepare call
         PendingIntentRecord originatingPendingIntent = mPendingIntentRecord;
-        BackgroundStartPrivileges forcedBalByPiSender = BackgroundStartPrivileges.NONE;
+        boolean allowBalExemptionForSystemProcess = false;
         Intent intent = TEST_INTENT;
         ActivityOptions checkedOptions = mCheckedOptions;
         BackgroundActivityStartController.BalState balState = mController.new BalState(callingUid,
                 callingPid, callingPackage, realCallingUid, realCallingPid, null,
-                originatingPendingIntent, forcedBalByPiSender, mResultRecord, intent,
+                originatingPendingIntent, allowBalExemptionForSystemProcess, mResultRecord, intent,
                 checkedOptions);
 
         // call
@@ -795,12 +794,12 @@
 
         // prepare call
         PendingIntentRecord originatingPendingIntent = mPendingIntentRecord;
-        BackgroundStartPrivileges forcedBalByPiSender = BackgroundStartPrivileges.NONE;
+        boolean allowBalExemptionForSystemProcess = false;
         Intent intent = TEST_INTENT;
         ActivityOptions checkedOptions = mCheckedOptions;
         BackgroundActivityStartController.BalState balState = mController.new BalState(callingUid,
                 callingPid, callingPackage, realCallingUid, realCallingPid, null,
-                originatingPendingIntent, forcedBalByPiSender, mResultRecord, intent,
+                originatingPendingIntent, allowBalExemptionForSystemProcess, mResultRecord, intent,
                 checkedOptions);
 
         // call
@@ -831,12 +830,12 @@
 
         // prepare call
         PendingIntentRecord originatingPendingIntent = mPendingIntentRecord;
-        BackgroundStartPrivileges forcedBalByPiSender = BackgroundStartPrivileges.NONE;
+        boolean allowBalExemptionForSystemProcess = false;
         Intent intent = TEST_INTENT;
         ActivityOptions checkedOptions = mCheckedOptions;
         BackgroundActivityStartController.BalState balState = mController.new BalState(callingUid,
                 callingPid, callingPackage, realCallingUid, realCallingPid, null,
-                originatingPendingIntent, forcedBalByPiSender, mResultRecord, intent,
+                originatingPendingIntent, allowBalExemptionForSystemProcess, mResultRecord, intent,
                 checkedOptions);
 
         // call
diff --git a/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerLogTests.java b/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerLogTests.java
index 23b1c4b..7f7462d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerLogTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerLogTests.java
@@ -23,7 +23,6 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import android.app.ActivityOptions;
-import android.app.BackgroundStartPrivileges;
 import android.content.Intent;
 import android.platform.test.annotations.Presubmit;
 
@@ -189,7 +188,7 @@
     private void useIntent(int uid) {
         mState = mController.new BalState(uid, APP1_PID,
                 "calling.package", uid, APP1_PID, null,
-                null, BackgroundStartPrivileges.NONE, null, new Intent(),
+                null, false, null, new Intent(),
                 ActivityOptions.makeBasic());
     }
 
@@ -200,7 +199,7 @@
     private void usePendingIntent(int callerUid, int realCallerUid) {
         mState = mController.new BalState(callerUid, APP1_PID,
                 "calling.package", realCallerUid, APP2_PID, null,
-                mPendingIntentRecord, BackgroundStartPrivileges.NONE, null, new Intent(),
+                mPendingIntentRecord, false, null, new Intent(),
                 ActivityOptions.makeBasic());
     }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerTests.java
index 6ec7895..db3ce0b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerTests.java
@@ -194,6 +194,7 @@
         mService.mTaskSupervisor = mSupervisor;
         mService.mContext = mContext;
         setViaReflection(mService, "mActiveUids", mActiveUids);
+        setViaReflection(mService, "mGlobalLock", new WindowManagerGlobalLock());
         Mockito.when(mService.getPackageManagerInternalLocked()).thenReturn(
                 mPackageManagerInternal);
         mService.mRootWindowContainer = mRootWindowContainer;
@@ -241,14 +242,14 @@
         int realCallingUid = NO_UID;
         int realCallingPid = NO_PID;
         PendingIntentRecord originatingPendingIntent = null;
-        BackgroundStartPrivileges forcedBalByPiSender = BackgroundStartPrivileges.NONE;
+        boolean allowBalExemptionForSystemProcess = false;
         Intent intent = TEST_INTENT;
         ActivityOptions checkedOptions = ActivityOptions.makeBasic();
 
         // call
         BalVerdict verdict = mController.checkBackgroundActivityStart(callingUid, callingPid,
                 callingPackage, realCallingUid, realCallingPid, mCallerApp,
-                originatingPendingIntent, forcedBalByPiSender, mResultRecord, intent,
+                originatingPendingIntent, allowBalExemptionForSystemProcess, mResultRecord, intent,
                 checkedOptions);
 
         // assertions
@@ -276,14 +277,14 @@
         int realCallingUid = NO_UID;
         int realCallingPid = NO_PID;
         PendingIntentRecord originatingPendingIntent = null;
-        BackgroundStartPrivileges forcedBalByPiSender = BackgroundStartPrivileges.NONE;
+        boolean allowBalExemptionForSystemProcess = false;
         Intent intent = TEST_INTENT;
         ActivityOptions checkedOptions = ActivityOptions.makeBasic();
 
         // call
         BalVerdict verdict = mController.checkBackgroundActivityStart(callingUid, callingPid,
                 callingPackage, realCallingUid, realCallingPid, mCallerApp,
-                originatingPendingIntent, forcedBalByPiSender, mResultRecord, intent,
+                originatingPendingIntent, allowBalExemptionForSystemProcess, mResultRecord, intent,
                 checkedOptions);
 
         // assertions
@@ -311,14 +312,14 @@
         int realCallingUid = NO_UID;
         int realCallingPid = NO_PID;
         PendingIntentRecord originatingPendingIntent = null;
-        BackgroundStartPrivileges forcedBalByPiSender = BackgroundStartPrivileges.NONE;
+        boolean allowBalExemptionForSystemProcess = false;
         Intent intent = TEST_INTENT;
         ActivityOptions checkedOptions = ActivityOptions.makeBasic();
 
         // call
         BalVerdict verdict = mController.checkBackgroundActivityStart(callingUid, callingPid,
                 callingPackage, realCallingUid, realCallingPid, mCallerApp,
-                originatingPendingIntent, forcedBalByPiSender, mResultRecord, intent,
+                originatingPendingIntent, allowBalExemptionForSystemProcess, mResultRecord, intent,
                 checkedOptions);
 
         // assertions
@@ -346,14 +347,14 @@
         int realCallingUid = NO_UID;
         int realCallingPid = NO_PID;
         PendingIntentRecord originatingPendingIntent = null;
-        BackgroundStartPrivileges forcedBalByPiSender = BackgroundStartPrivileges.NONE;
+        boolean allowBalExemptionForSystemProcess = false;
         Intent intent = TEST_INTENT;
         ActivityOptions checkedOptions = ActivityOptions.makeBasic();
 
         // call
         BalVerdict verdict = mController.checkBackgroundActivityStart(callingUid, callingPid,
                 callingPackage, realCallingUid, realCallingPid, mCallerApp,
-                originatingPendingIntent, forcedBalByPiSender, mResultRecord, intent,
+                originatingPendingIntent, allowBalExemptionForSystemProcess, mResultRecord, intent,
                 checkedOptions);
 
         // assertions
@@ -380,14 +381,14 @@
         int realCallingUid = NO_UID;
         int realCallingPid = NO_PID;
         PendingIntentRecord originatingPendingIntent = null;
-        BackgroundStartPrivileges forcedBalByPiSender = BackgroundStartPrivileges.NONE;
+        boolean allowBalExemptionForSystemProcess = false;
         Intent intent = TEST_INTENT;
         ActivityOptions checkedOptions = ActivityOptions.makeBasic();
 
         // call
         BalVerdict verdict = mController.checkBackgroundActivityStart(callingUid, callingPid,
                 callingPackage, realCallingUid, realCallingPid, mCallerApp,
-                originatingPendingIntent, forcedBalByPiSender, mResultRecord, intent,
+                originatingPendingIntent, allowBalExemptionForSystemProcess, mResultRecord, intent,
                 checkedOptions);
 
         // assertions
@@ -418,7 +419,7 @@
         int realCallingUid = NO_UID;
         int realCallingPid = NO_PID;
         PendingIntentRecord originatingPendingIntent = mPendingIntentRecord;
-        BackgroundStartPrivileges forcedBalByPiSender = BackgroundStartPrivileges.NONE;
+        boolean allowBalExemptionForSystemProcess = false;
         Intent intent = TEST_INTENT;
         ActivityOptions checkedOptions = ActivityOptions.makeBasic()
                 .setPendingIntentBackgroundActivityStartMode(
@@ -429,7 +430,7 @@
         // call
         BalVerdict verdict = mController.checkBackgroundActivityStart(callingUid, callingPid,
                 callingPackage, realCallingUid, realCallingPid, mCallerApp,
-                originatingPendingIntent, forcedBalByPiSender, mResultRecord, intent,
+                originatingPendingIntent, allowBalExemptionForSystemProcess, mResultRecord, intent,
                 checkedOptions);
 
         // assertions
@@ -457,7 +458,7 @@
         int realCallingUid = NO_UID;
         int realCallingPid = NO_PID;
         PendingIntentRecord originatingPendingIntent = mPendingIntentRecord;
-        BackgroundStartPrivileges forcedBalByPiSender = BackgroundStartPrivileges.NONE;
+        boolean allowBalExemptionForSystemProcess = false;
         Intent intent = TEST_INTENT;
         ActivityOptions checkedOptions = ActivityOptions.makeBasic()
                 .setPendingIntentCreatorBackgroundActivityStartMode(
@@ -466,7 +467,7 @@
         // call
         BalVerdict verdict = mController.checkBackgroundActivityStart(callingUid, callingPid,
                 callingPackage, realCallingUid, realCallingPid, mCallerApp,
-                originatingPendingIntent, forcedBalByPiSender, mResultRecord, intent,
+                originatingPendingIntent, allowBalExemptionForSystemProcess, mResultRecord, intent,
                 checkedOptions);
 
         // assertions
@@ -494,7 +495,7 @@
         int realCallingUid = NO_UID;
         int realCallingPid = NO_PID;
         PendingIntentRecord originatingPendingIntent = mPendingIntentRecord;
-        BackgroundStartPrivileges forcedBalByPiSender = BackgroundStartPrivileges.NONE;
+        boolean allowBalExemptionForSystemProcess = false;
         Intent intent = TEST_INTENT;
         ActivityOptions checkedOptions = ActivityOptions.makeBasic()
                 .setPendingIntentBackgroundActivityStartMode(
@@ -503,7 +504,7 @@
         // call
         BalVerdict verdict = mController.checkBackgroundActivityStart(callingUid, callingPid,
                 callingPackage, realCallingUid, realCallingPid, mCallerApp,
-                originatingPendingIntent, forcedBalByPiSender, mResultRecord, intent,
+                originatingPendingIntent, allowBalExemptionForSystemProcess, mResultRecord, intent,
                 checkedOptions);
 
         // assertions
@@ -530,7 +531,7 @@
         int realCallingUid = NO_UID;
         int realCallingPid = NO_PID;
         PendingIntentRecord originatingPendingIntent = null;
-        BackgroundStartPrivileges forcedBalByPiSender = BackgroundStartPrivileges.NONE;
+        boolean allowBalExemptionForSystemProcess = false;
         Intent intent = TEST_INTENT;
         ActivityOptions checkedOptions = ActivityOptions.makeBasic();
         WindowProcessController callerApp = mCallerApp;
@@ -539,8 +540,8 @@
         // call
         BackgroundActivityStartController.BalState balState = mController
                 .new BalState(callingUid, callingPid, callingPackage, realCallingUid,
-                realCallingPid, callerApp, originatingPendingIntent, forcedBalByPiSender,
-                resultRecord, intent, checkedOptions);
+                realCallingPid, callerApp, originatingPendingIntent,
+                allowBalExemptionForSystemProcess, resultRecord, intent, checkedOptions);
 
         // assertions
         assertThat(balState.mAutoOptInReason).isEqualTo("notPendingIntent");
@@ -559,7 +560,7 @@
                         + "callingUidHasAnyVisibleWindow: false; "
                         + "callingUidProcState: NONEXISTENT; "
                         + "isCallingUidPersistentSystemProcess: false; "
-                        + "forcedBalByPiSender: BSP.NONE; "
+                        + "allowBalExemptionForSystemProcess: false; "
                         + "intent: Intent { cmp=package.app3/someClass }; "
                         + "callerApp: mCallerApp; "
                         + "inVisibleTask: false; "
@@ -596,7 +597,7 @@
         int realCallingUid = NO_UID;
         int realCallingPid = NO_PID;
         PendingIntentRecord originatingPendingIntent = mPendingIntentRecord;
-        BackgroundStartPrivileges forcedBalByPiSender = BackgroundStartPrivileges.NONE;
+        boolean allowBalExemptionForSystemProcess = false;
         Intent intent = TEST_INTENT;
         ActivityOptions checkedOptions = ActivityOptions.makeBasic();
         WindowProcessController callerApp = mCallerApp;
@@ -605,8 +606,8 @@
         // call
         BackgroundActivityStartController.BalState balState = mController
                 .new BalState(callingUid, callingPid, callingPackage, realCallingUid,
-                realCallingPid, callerApp, originatingPendingIntent, forcedBalByPiSender,
-                resultRecord, intent, checkedOptions);
+                realCallingPid, callerApp, originatingPendingIntent,
+                allowBalExemptionForSystemProcess, resultRecord, intent, checkedOptions);
 
         // assertions
         assertThat(balState.mAutoOptInReason).isEqualTo("callForResult");
@@ -629,7 +630,7 @@
         int realCallingUid = NO_UID;
         int realCallingPid = NO_PID;
         PendingIntentRecord originatingPendingIntent = mPendingIntentRecord;
-        BackgroundStartPrivileges forcedBalByPiSender = BackgroundStartPrivileges.NONE;
+        boolean allowBalExemptionForSystemProcess = false;
         Intent intent = TEST_INTENT;
         ActivityOptions checkedOptions = ActivityOptions.makeBasic();
         WindowProcessController callerApp = mCallerApp;
@@ -638,8 +639,8 @@
         // call
         BackgroundActivityStartController.BalState balState = mController
                 .new BalState(callingUid, callingPid, callingPackage, realCallingUid,
-                realCallingPid, callerApp, originatingPendingIntent, forcedBalByPiSender,
-                resultRecord, intent, checkedOptions);
+                realCallingPid, callerApp, originatingPendingIntent,
+                allowBalExemptionForSystemProcess, resultRecord, intent, checkedOptions);
 
         // assertions
         assertThat(balState.mAutoOptInReason).isNull();
@@ -659,7 +660,7 @@
                         + "callingUidHasAnyVisibleWindow: false; "
                         + "callingUidProcState: NONEXISTENT; "
                         + "isCallingUidPersistentSystemProcess: false; "
-                        + "forcedBalByPiSender: BSP.NONE; "
+                        + "allowBalExemptionForSystemProcess: false; "
                         + "intent: Intent { cmp=package.app3/someClass }; "
                         + "callerApp: mCallerApp; "
                         + "inVisibleTask: false; "
diff --git a/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java b/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java
index 1f3aa35..a30591e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java
@@ -23,6 +23,7 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
 import static com.android.server.wm.utils.LastCallVerifier.lastCall;
 
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
@@ -33,6 +34,7 @@
 import android.graphics.Rect;
 import android.platform.test.annotations.Presubmit;
 import android.platform.test.annotations.RequiresFlagsDisabled;
+import android.platform.test.annotations.RequiresFlagsEnabled;
 import android.view.SurfaceControl;
 import android.view.SurfaceSession;
 
@@ -56,6 +58,7 @@
         final SurfaceSession mSession = new SurfaceSession();
         final SurfaceControl mHostControl = mock(SurfaceControl.class);
         final SurfaceControl.Transaction mHostTransaction = spy(StubTransaction.class);
+        Rect mBounds = new Rect(10, 20, 30, 40);
 
         MockSurfaceBuildingContainer(WindowManagerService wm) {
             super(wm);
@@ -94,6 +97,11 @@
         public SurfaceControl.Transaction getPendingTransaction() {
             return mHostTransaction;
         }
+
+        @Override
+        public Rect getBounds() {
+            return mBounds;
+        }
     }
 
     static class MockAnimationAdapterFactory extends DimmerAnimationHelper.AnimationAdapterFactory {
@@ -104,34 +112,63 @@
         }
     }
 
+    static class TestActivityEmbeddingMock {
+        Task mTask = mock(Task.class);
+        TaskFragment mLeft = mock(TaskFragment.class);
+        TaskFragment mRight = mock(TaskFragment.class);
+        Rect mTaskBounds = new Rect(10, 0, 50, 40);
+        Rect mLeftBounds = new Rect(10, 0, 30, 40);
+        Rect mRightBounds = new Rect(30, 0, 50, 40);
+
+        TestActivityEmbeddingMock() {
+            when(mTask.getBounds()).thenReturn(mTaskBounds);
+            when(mLeft.getBounds()).thenReturn(mLeftBounds);
+            when(mRight.getBounds()).thenReturn(mRightBounds);
+            when(mLeft.isEmbedded()).thenReturn(true);
+            when(mRight.isEmbedded()).thenReturn(true);
+        }
+
+        void pretendParentToTask(WindowState child) {
+            when(child.getTaskFragment()).thenReturn(mTask);
+            when(child.getTask()).thenReturn(mTask);
+        }
+
+        void pretendParentToRight(WindowState child) {
+            when(child.getTaskFragment()).thenReturn(mRight);
+            when(child.getTask()).thenReturn(mTask);
+        }
+    }
+
+    WindowState getMockDimmingContainer() {
+        WindowState window = mock(WindowState.class);
+        SurfaceControl surface = mock(SurfaceControl.class);
+        when(window.getSurfaceControl()).thenReturn(surface);
+        return window;
+    }
+
     private Dimmer mDimmer;
     private SurfaceControl.Transaction mTransaction;
+    MockSurfaceBuildingContainer mHost;
     private WindowState mChild1;
     private WindowState mChild2;
     private static AnimationAdapter sTestAnimation;
 
     @Before
     public void setUp() throws Exception {
-        MockSurfaceBuildingContainer host = new MockSurfaceBuildingContainer(mWm);
-        mTransaction = host.getSyncTransaction();
-
-        final SurfaceControl mControl1 = mock(SurfaceControl.class);
-        final SurfaceControl mControl2 = mock(SurfaceControl.class);
+        mHost = new MockSurfaceBuildingContainer(mWm);
+        mTransaction = mHost.getSyncTransaction();
 
         SurfaceAnimator animator = mock(SurfaceAnimator.class);
         when(animator.getAnimation()).thenReturn(null);
 
-        mChild1 = mock(WindowState.class);
-        when(mChild1.getSurfaceControl()).thenReturn(mControl1);
+        mChild1 = getMockDimmingContainer();
+        mChild2 = getMockDimmingContainer();
 
-        mChild2 = mock(WindowState.class);
-        when(mChild2.getSurfaceControl()).thenReturn(mControl2);
-
-        host.addChild(mChild1, 0);
-        host.addChild(mChild2, 1);
+        mHost.addChild(mChild1, 0);
+        mHost.addChild(mChild2, 1);
 
         sTestAnimation = spy(new MockAnimationAdapter());
-        mDimmer = new Dimmer(host, new MockAnimationAdapterFactory());
+        mDimmer = new Dimmer(mHost, new MockAnimationAdapterFactory());
     }
 
     @Test
@@ -150,6 +187,63 @@
     }
 
     @Test
+    @RequiresFlagsEnabled(Flags.FLAG_USE_TASKS_DIM_ONLY)
+    public void testBoundsInActivityEmbeddingForWholeTask() {
+        final WindowState dimmingWindow = getMockDimmingContainer();
+        TestActivityEmbeddingMock embedding = new TestActivityEmbeddingMock();
+        embedding.pretendParentToRight(dimmingWindow);
+        when(embedding.mRight.isDimmingOnParentTask()).thenReturn(true);
+
+        mDimmer.adjustAppearance(dimmingWindow, 1, 1);
+        mDimmer.adjustPosition(dimmingWindow, dimmingWindow);
+        mDimmer.updateDims(mTransaction);
+        verify(mTransaction).setWindowCrop(mDimmer.getDimLayer(),
+                embedding.mTaskBounds.width(), embedding.mTaskBounds.height());
+        verify(mTransaction).setPosition(mDimmer.getDimLayer(), 0, 0);
+    }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_USE_TASKS_DIM_ONLY)
+    public void testBoundsInActivityEmbeddingForTaskFragmentOnly() {
+        final WindowState dimmingWindow = getMockDimmingContainer();
+        TestActivityEmbeddingMock embedding = new TestActivityEmbeddingMock();
+        embedding.pretendParentToRight(dimmingWindow);
+        when(embedding.mRight.isDimmingOnParentTask()).thenReturn(false);
+
+        mDimmer.adjustAppearance(dimmingWindow, 1, 1);
+        mDimmer.adjustPosition(dimmingWindow, dimmingWindow);
+        mDimmer.updateDims(mTransaction);
+        Rect expectedAbsoluteBounds = embedding.mRightBounds;
+        Rect expectedRelativeBounds = new Rect(expectedAbsoluteBounds);
+        expectedRelativeBounds.offset(-embedding.mTaskBounds.left, -embedding.mTaskBounds.top);
+        verify(mTransaction).setWindowCrop(mDimmer.getDimLayer(),
+                embedding.mRightBounds.width(), embedding.mRightBounds.height());
+        verify(mTransaction).setPosition(mDimmer.getDimLayer(),
+                expectedRelativeBounds.left, expectedRelativeBounds.top);
+        assertEquals(expectedAbsoluteBounds, mDimmer.getDimBounds());
+    }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_USE_TASKS_DIM_ONLY)
+    public void testDimBoundsAdaptToResizing() {
+        // First call with some generic bounds
+        mDimmer.adjustAppearance(mChild1, 0.5f, 1);
+        mDimmer.adjustPosition(mChild1, mChild1);
+        mDimmer.updateDims(mTransaction);
+        verify(mTransaction).setWindowCrop(mDimmer.getDimLayer(),
+                mHost.getBounds().width(), mHost.getBounds().height());
+
+        // Change bounds
+        mHost.getBounds().left += 5;
+        mHost.getBounds().top += 6;
+        mDimmer.adjustAppearance(mChild1, 1, 1);
+        mDimmer.adjustPosition(mChild1, mChild1);
+        mDimmer.updateDims(mTransaction);
+        verify(mTransaction).setWindowCrop(mDimmer.getDimLayer(),
+                mHost.getBounds().width(), mHost.getBounds().height());
+    }
+
+    @Test
     public void testDimBelowWithChildSurfaceCreatesSurfaceBelowChild() {
         final float alpha = 0.7f;
         final int blur = 50;
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index 2bebcc3..9408f90 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -1609,8 +1609,10 @@
 
         final ActivityRecord app = mAppWindow.mActivityRecord;
         app.setVisible(false);
-        mDisplayContent.prepareAppTransition(WindowManager.TRANSIT_OPEN);
-        mDisplayContent.mOpeningApps.add(app);
+        app.setVisibleRequested(false);
+        registerTestTransitionPlayer();
+        mDisplayContent.requestTransitionAndLegacyPrepare(WindowManager.TRANSIT_OPEN, 0);
+        app.setVisibility(true);
         final int newOrientation = getRotatedOrientation(mDisplayContent);
         app.setRequestedOrientation(newOrientation);
 
@@ -1641,14 +1643,6 @@
         assertEquals(state.isSourceOrDefaultVisible(statusBarId, statusBars()),
                 rotatedState.isSourceOrDefaultVisible(statusBarId, statusBars()));
 
-        final Rect outFrame = new Rect();
-        final Rect outInsets = new Rect();
-        final Rect outStableInsets = new Rect();
-        final Rect outSurfaceInsets = new Rect();
-        mAppWindow.getAnimationFrames(outFrame, outInsets, outStableInsets, outSurfaceInsets);
-        // The animation frames should not be rotated because display hasn't rotated.
-        assertEquals(mDisplayContent.getBounds(), outFrame);
-
         // The display should keep current orientation and the rotated configuration should apply
         // to the activity.
         assertEquals(config.orientation, mDisplayContent.getConfiguration().orientation);
@@ -1676,9 +1670,8 @@
         // Launch another activity before the transition is finished.
         final Task task2 = new TaskBuilder(mSupervisor).setDisplay(mDisplayContent).build();
         final ActivityRecord app2 = new ActivityBuilder(mAtm).setTask(task2)
-                .setUseProcess(app.app).build();
-        app2.setVisible(false);
-        mDisplayContent.mOpeningApps.add(app2);
+                .setUseProcess(app.app).setVisible(false).build();
+        app2.setVisibility(true);
         app2.setRequestedOrientation(newOrientation);
 
         // The activity should share the same transform state as the existing one. The activity
@@ -1701,14 +1694,15 @@
         assertTrue(mImeWindow.isAnimating(PARENTS, ANIMATION_TYPE_TOKEN_TRANSFORM));
 
         // The fixed rotation transform can only be finished when all animation finished.
-        doReturn(false).when(app2).isAnimating(anyInt(), anyInt());
-        mDisplayContent.mAppTransition.notifyAppTransitionFinishedLocked(app2.token);
+        doReturn(false).when(app2).inTransition();
+        mDisplayContent.mFixedRotationTransitionListener.onAppTransitionFinishedLocked(app2.token);
         assertTrue(app.hasFixedRotationTransform());
         assertTrue(app2.hasFixedRotationTransform());
 
         // The display should be rotated after the launch is finished.
-        doReturn(false).when(app).isAnimating(anyInt(), anyInt());
-        mDisplayContent.mAppTransition.notifyAppTransitionFinishedLocked(app.token);
+        app.setVisible(true);
+        doReturn(false).when(app).inTransition();
+        mDisplayContent.mFixedRotationTransitionListener.onAppTransitionFinishedLocked(app.token);
         mStatusBarWindow.finishSeamlessRotation(t);
         mNavBarWindow.finishSeamlessRotation(t);
 
@@ -1726,7 +1720,7 @@
         final Task task = app.getTask();
         final ActivityRecord app2 = new ActivityBuilder(mWm.mAtmService).setTask(task).build();
         mDisplayContent.setFixedRotationLaunchingApp(app2, (mDisplayContent.getRotation() + 1) % 4);
-        doReturn(true).when(app).inTransitionSelfOrParent();
+        doReturn(true).when(app).inTransition();
         // If the task contains a transition, this should be no-op.
         mDisplayContent.mFixedRotationTransitionListener.onAppTransitionFinishedLocked(app.token);
 
@@ -1736,7 +1730,7 @@
         // The display should be unlikely to be in transition, but if it happens, the fixed
         // rotation should proceed to finish because the activity/task level transition is finished.
         doReturn(true).when(mDisplayContent).inTransition();
-        doReturn(false).when(app).inTransitionSelfOrParent();
+        doReturn(false).when(app).inTransition();
         // Although this notifies app instead of app2 that uses the fixed rotation, app2 should
         // still finish the transform because there is no more transition event.
         mDisplayContent.mFixedRotationTransitionListener.onAppTransitionFinishedLocked(app.token);
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
index eb8bc91..27d46fc 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
@@ -16,10 +16,8 @@
 
 package com.android.server.wm;
 
-import static android.inputmethodservice.InputMethodService.ENABLE_HIDE_IME_CAPTION_BAR;
 import static android.view.DisplayCutout.NO_CUTOUT;
 import static android.view.InsetsSource.ID_IME;
-import static android.view.RoundedCorners.NO_ROUNDED_CORNERS;
 import static android.view.Surface.ROTATION_0;
 import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
 import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
@@ -57,14 +55,11 @@
 import android.graphics.Rect;
 import android.platform.test.annotations.Presubmit;
 import android.view.DisplayInfo;
-import android.view.DisplayShape;
 import android.view.InsetsFrameProvider;
 import android.view.InsetsSource;
 import android.view.InsetsState;
-import android.view.PrivacyIndicatorBounds;
 import android.view.Surface;
 import android.view.WindowInsets;
-import android.view.WindowInsets.Side;
 import android.view.WindowManager;
 
 import androidx.test.filters.SmallTest;
@@ -495,44 +490,6 @@
                 di.logicalHeight).mNonDecorInsets.bottom);
     }
 
-    @SetupWindows(addWindows = { W_NAVIGATION_BAR, W_INPUT_METHOD })
-    @Test
-    public void testImeMinimalSourceFrame() {
-        Assume.assumeFalse("Behavior no longer needed with ENABLE_HIDE_IME_CAPTION_BAR",
-                ENABLE_HIDE_IME_CAPTION_BAR);
-
-        final DisplayPolicy displayPolicy = mDisplayContent.getDisplayPolicy();
-        final DisplayInfo displayInfo = mDisplayContent.getDisplayInfo();
-
-        WindowManager.LayoutParams attrs = mNavBarWindow.mAttrs;
-        displayPolicy.addWindowLw(mNavBarWindow, attrs);
-        mNavBarWindow.setRequestedSize(attrs.width, attrs.height);
-        mNavBarWindow.getControllableInsetProvider().setServerVisible(true);
-        final InsetsState state = mDisplayContent.getInsetsStateController().getRawInsetsState();
-        mImeWindow.mAboveInsetsState.set(state);
-        mDisplayContent.mDisplayFrames = new DisplayFrames(
-                state, displayInfo, NO_CUTOUT, NO_ROUNDED_CORNERS, new PrivacyIndicatorBounds(),
-                DisplayShape.NONE);
-
-        mDisplayContent.setInputMethodWindowLocked(mImeWindow);
-        mImeWindow.mAttrs.setFitInsetsSides(Side.all() & ~Side.BOTTOM);
-        mImeWindow.mGivenContentInsets.set(0, displayInfo.logicalHeight, 0, 0);
-        mImeWindow.getControllableInsetProvider().setServerVisible(true);
-
-        displayPolicy.layoutWindowLw(mNavBarWindow, null, mDisplayContent.mDisplayFrames);
-        displayPolicy.layoutWindowLw(mImeWindow, null, mDisplayContent.mDisplayFrames);
-
-        final InsetsSource imeSource = state.peekSource(ID_IME);
-        final InsetsSource navBarSource = state.peekSource(
-                mNavBarWindow.getControllableInsetProvider().getSource().getId());
-
-        assertNotNull(imeSource);
-        assertNotNull(navBarSource);
-        assertFalse(imeSource.getFrame().isEmpty());
-        assertFalse(navBarSource.getFrame().isEmpty());
-        assertTrue(imeSource.getFrame().contains(navBarSource.getFrame()));
-    }
-
     @SetupWindows(addWindows = W_INPUT_METHOD)
     @Test
     public void testImeInsetsGivenContentFrame() {
diff --git a/services/tests/wmtests/src/com/android/server/wm/SafeActivityOptionsTest.java b/services/tests/wmtests/src/com/android/server/wm/SafeActivityOptionsTest.java
index 55a7089..791b5b5 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SafeActivityOptionsTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SafeActivityOptionsTest.java
@@ -35,6 +35,7 @@
 
 import android.app.ActivityOptions;
 import android.content.pm.ActivityInfo;
+import android.os.Binder;
 import android.os.Looper;
 import android.platform.test.annotations.Presubmit;
 import android.view.RemoteAnimationAdapter;
@@ -61,7 +62,8 @@
         opts1.setLaunchDisplayId(5);
         final ActivityOptions opts2 = ActivityOptions.makeBasic();
         opts2.setLaunchDisplayId(6);
-        final SafeActivityOptions options = new SafeActivityOptions(opts1);
+        final SafeActivityOptions options = new SafeActivityOptions(opts1,
+                Binder.getCallingPid(), Binder.getCallingUid());
         final ActivityOptions result = options.mergeActivityOptions(opts1, opts2);
         assertEquals(6, result.getLaunchDisplayId());
     }
@@ -75,7 +77,8 @@
         final SafeActivityOptions clone = new SafeActivityOptions(ActivityOptions.makeBasic()
                 .setLaunchTaskDisplayArea(token)
                 .setLaunchDisplayId(launchDisplayId)
-                .setCallerDisplayId(callerDisplayId))
+                .setCallerDisplayId(callerDisplayId),
+                Binder.getCallingPid(), Binder.getCallingUid())
                 .selectiveCloneLaunchOptions();
 
         assertSame(clone.getOriginalOptions().getLaunchTaskDisplayArea(), token);
@@ -86,8 +89,9 @@
     @Test
     public void test_selectiveCloneLunchRootTask() {
         final WindowContainerToken token = mock(WindowContainerToken.class);
-        final SafeActivityOptions clone = new SafeActivityOptions(ActivityOptions.makeBasic()
-                .setLaunchRootTask(token))
+        final SafeActivityOptions clone = new SafeActivityOptions(
+                ActivityOptions.makeBasic().setLaunchRootTask(token),
+                Binder.getCallingPid(), Binder.getCallingUid())
                 .selectiveCloneLaunchOptions();
 
         assertSame(clone.getOriginalOptions().getLaunchRootTask(), token);
@@ -97,7 +101,8 @@
     public void test_selectiveCloneLunchRemoteTransition() {
         final RemoteTransition transition = mock(RemoteTransition.class);
         final SafeActivityOptions clone = new SafeActivityOptions(
-                ActivityOptions.makeRemoteTransition(transition))
+                ActivityOptions.makeRemoteTransition(transition),
+                Binder.getCallingPid(), Binder.getCallingUid())
                 .selectiveCloneLaunchOptions();
 
         assertSame(clone.getOriginalOptions().getRemoteTransition(), transition);
@@ -202,7 +207,8 @@
 
     private void verifySecureExceptionThrown(ActivityOptions activityOptions,
             ActivityTaskSupervisor taskSupervisor, TaskDisplayArea mockTda) {
-        SafeActivityOptions safeActivityOptions = new SafeActivityOptions(activityOptions);
+        SafeActivityOptions safeActivityOptions = new SafeActivityOptions(activityOptions,
+                Binder.getCallingPid(), Binder.getCallingUid());
         if (mockTda != null) {
             spyOn(safeActivityOptions);
             doReturn(mockTda).when(safeActivityOptions).getLaunchTaskDisplayArea(any(), any());
diff --git a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
index 7196acc..e8779c2 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
@@ -2354,6 +2354,11 @@
         // ChangeInfo#mCommonAncestor should be set after reparent.
         final Transition.ChangeInfo change = transition.mChanges.get(activity);
         assertEquals(newParent.getDisplayArea(), change.mCommonAncestor);
+
+        // WindowContainer#onDisplayChanged should collect the moved task.
+        final DisplayContent newDisplay = createNewDisplay();
+        newParent.reparent(newDisplay.getDefaultTaskDisplayArea(), true /* onTop */);
+        assertTrue(transition.mParticipants.contains(newParent));
     }
 
     @Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
index e7e184c..1750a14 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
@@ -406,11 +406,11 @@
         final var powerManager = mWm.mPowerManager;
         clearInvocations(powerManager);
         firstWindow.prepareWindowToDisplayDuringRelayout(false /*wasVisible*/);
-        verify(powerManager).wakeUp(anyLong(), anyInt(), anyString());
+        verify(powerManager).wakeUp(anyLong(), anyInt(), anyString(), anyInt());
 
         clearInvocations(powerManager);
         secondWindow.prepareWindowToDisplayDuringRelayout(false /*wasVisible*/);
-        verify(powerManager).wakeUp(anyLong(), anyInt(), anyString());
+        verify(powerManager).wakeUp(anyLong(), anyInt(), anyString(), anyInt());
     }
 
     private void testPrepareWindowToDisplayDuringRelayout(WindowState appWindow,
@@ -420,9 +420,9 @@
         appWindow.prepareWindowToDisplayDuringRelayout(false /* wasVisible */);
 
         if (expectedWakeupCalled) {
-            verify(powerManager).wakeUp(anyLong(), anyInt(), anyString());
+            verify(powerManager).wakeUp(anyLong(), anyInt(), anyString(), anyInt());
         } else {
-            verify(powerManager, never()).wakeUp(anyLong(), anyInt(), anyString());
+            verify(powerManager, never()).wakeUp(anyLong(), anyInt(), anyString(), anyInt());
         }
         // If wakeup is expected to be called, the currentLaunchCanTurnScreenOn should be false
         // because the state will be consumed.
diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerHw3Compat.java b/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerHw3Compat.java
index b1165bb..dfd80a0 100644
--- a/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerHw3Compat.java
+++ b/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerHw3Compat.java
@@ -36,6 +36,8 @@
 import android.os.ServiceSpecificException;
 import android.os.SystemClock;
 
+import com.android.server.FgThread;
+
 public class SoundTriggerHw3Compat implements ISoundTriggerHal {
     private final @NonNull ISoundTriggerHw mDriver;
     private final @NonNull Runnable mRebootRunnable;
@@ -217,7 +219,12 @@
 
         @Override
         public void onResourcesAvailable() {
-            mDelegate.onResourcesAvailable();
+            // This call does not need to be sequenced relative to sessions on the upper levels.
+            // That is, if a new session gets this callback or if a already detached session gets
+            // this callback, because it is delayed, it doesn't matter, since this callback is
+            // purely informative and does not mutate any state -- it merely causes an already legal
+            // operation to be possibly re-attempted.
+            FgThread.getExecutor().execute(mDelegate::onResourcesAvailable);
         }
 
         @Override
diff --git a/telecomm/java/android/telecom/Conference.java b/telecomm/java/android/telecom/Conference.java
index f803717..7adcd46 100644
--- a/telecomm/java/android/telecom/Conference.java
+++ b/telecomm/java/android/telecom/Conference.java
@@ -101,7 +101,7 @@
     private Set<String> mPreviousExtraKeys;
     private final Object mExtrasLock = new Object();
     private Uri mAddress;
-    private int mAddressPresentation;
+    private int mAddressPresentation = TelecomManager.PRESENTATION_UNKNOWN;
     private String mCallerDisplayName;
     private int mCallerDisplayNamePresentation;
     private int mCallDirection;
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index ad7d987..29d3942 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -2164,7 +2164,7 @@
     private CallAudioState mCallAudioState;
     private CallEndpoint mCallEndpoint;
     private Uri mAddress;
-    private int mAddressPresentation;
+    private int mAddressPresentation = TelecomManager.PRESENTATION_UNKNOWN;
     private String mCallerDisplayName;
     private int mCallerDisplayNamePresentation;
     private boolean mRingbackRequested = false;
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index a7fe0cb..fad59f8b 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -17278,6 +17278,18 @@
     }
 
     /**
+     * Setup sISms for testing.
+     *
+     * @hide
+     */
+    @VisibleForTesting
+    public static void setupISmsForTest(ISms iSms) {
+        synchronized (sCacheLock) {
+            sISms = iSms;
+        }
+    }
+
+    /**
      * Whether device can connect to 5G network when two SIMs are active.
      *
      * @hide TODO b/153669716: remove or make system API.
diff --git a/telephony/java/android/telephony/satellite/SatelliteManager.java b/telephony/java/android/telephony/satellite/SatelliteManager.java
index f0c3504..c78dbe7 100644
--- a/telephony/java/android/telephony/satellite/SatelliteManager.java
+++ b/telephony/java/android/telephony/satellite/SatelliteManager.java
@@ -710,6 +710,16 @@
     public static final String ACTION_SATELLITE_SUBSCRIBER_ID_LIST_CHANGED =
             "android.telephony.action.ACTION_SATELLITE_SUBSCRIBER_ID_LIST_CHANGED";
 
+
+    /**
+     * This intent will be broadcasted to start a non-emergency session.
+     * This intent will be sent only to the app with component defined in
+     * config_satellite_carrier_roaming_non_emergency_session_class and package defined in
+     * config_satellite_gateway_service_package
+     * @hide
+     */
+    public static final String ACTION_SATELLITE_START_NON_EMERGENCY_SESSION =
+            "android.telephony.action.ACTION_SATELLITE_START_NON_EMERGENCY_SESSION";
     /**
      * Meta-data represents whether the application supports P2P SMS over carrier roaming satellite
      * which needs manual trigger to connect to satellite. The messaging applications that supports
diff --git a/tests/AppJankTest/Android.bp b/tests/AppJankTest/Android.bp
new file mode 100644
index 0000000..acf8dc9
--- /dev/null
+++ b/tests/AppJankTest/Android.bp
@@ -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 {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_test {
+    name: "CoreAppJankTestCases",
+    team: "trendy_team_system_performance",
+    srcs: ["src/**/*.java"],
+    static_libs: [
+        "androidx.test.rules",
+        "androidx.test.core",
+        "platform-test-annotations",
+        "flag-junit",
+    ],
+    platform_apis: true,
+    test_suites: ["device-tests"],
+    certificate: "platform",
+}
diff --git a/tests/AppJankTest/AndroidManifest.xml b/tests/AppJankTest/AndroidManifest.xml
new file mode 100644
index 0000000..ae97339
--- /dev/null
+++ b/tests/AppJankTest/AndroidManifest.xml
@@ -0,0 +1,40 @@
+<?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.
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="android.app.jank.tests">
+
+    <application>
+        <uses-library android:name="android.test.runner" />
+        <activity android:name=".EmptyActivity"
+                  android:label="EmptyActivity"
+                  android:exported="true">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN"/>
+                <action android:name="android.intent.action.VIEW_PERMISSION_USAGE"/>
+            </intent-filter>
+        </activity>
+    </application>
+
+    <!--  self-instrumenting test package. -->
+    <instrumentation
+        android:name="androidx.test.runner.AndroidJUnitRunner"
+        android:targetPackage="android.app.jank.tests"
+        android:label="Core tests of App Jank Tracking">
+    </instrumentation>
+
+</manifest>
\ No newline at end of file
diff --git a/tests/AppJankTest/AndroidTest.xml b/tests/AppJankTest/AndroidTest.xml
new file mode 100644
index 0000000..c01c75c
--- /dev/null
+++ b/tests/AppJankTest/AndroidTest.xml
@@ -0,0 +1,38 @@
+<?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.
+  -->
+<configuration description="Config for Core App Jank Tests">
+    <option name="test-suite-tag" value="apct"/>
+
+    <option name="config-descriptor:metadata" key="component" value="systems"/>
+    <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
+    <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
+    <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
+    <option name="config-descriptor:metadata" key="parameter" value="no_foldable_states" />
+
+    <option name="not-shardable" value="true" />
+    <option name="install-arg" value="-t" />
+
+    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+        <option name="cleanup-apks" value="true"/>
+        <option name="test-file-name" value="CoreAppJankTestCases.apk"/>
+    </target_preparer>
+
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest">
+        <option name="package" value="android.app.jank.tests"/>
+        <option name="runner" value="androidx.test.runner.AndroidJUnitRunner"/>
+    </test>
+</configuration>
\ No newline at end of file
diff --git a/tests/AppJankTest/OWNERS b/tests/AppJankTest/OWNERS
new file mode 100644
index 0000000..806de57
--- /dev/null
+++ b/tests/AppJankTest/OWNERS
@@ -0,0 +1,4 @@
+steventerrell@google.com
+carmenjackson@google.com
+jjaggi@google.com
+pmuetschard@google.com
\ No newline at end of file
diff --git a/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodRunnerState.java b/tests/AppJankTest/src/android/app/jank/tests/EmptyActivity.java
similarity index 73%
copy from ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodRunnerState.java
copy to tests/AppJankTest/src/android/app/jank/tests/EmptyActivity.java
index 83cbc52..b326765 100644
--- a/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodRunnerState.java
+++ b/tests/AppJankTest/src/android/app/jank/tests/EmptyActivity.java
@@ -13,10 +13,10 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package android.platform.test.ravenwood;
 
-/** Stub class. The actual implementaetion is in junit-impl-src. */
-public class RavenwoodRunnerState {
-    public RavenwoodRunnerState(RavenwoodAwareTestRunner runner) {
-    }
+package android.app.jank.tests;
+
+import android.app.Activity;
+
+public class EmptyActivity extends Activity {
 }
diff --git a/tests/AppJankTest/src/android/app/jank/tests/StateTrackerTest.java b/tests/AppJankTest/src/android/app/jank/tests/StateTrackerTest.java
new file mode 100644
index 0000000..541009e
--- /dev/null
+++ b/tests/AppJankTest/src/android/app/jank/tests/StateTrackerTest.java
@@ -0,0 +1,227 @@
+/*
+ * 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.app.jank.tests;
+
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.app.jank.Flags;
+import android.app.jank.StateTracker;
+import android.app.jank.StateTracker.StateData;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
+import android.view.Choreographer;
+
+import androidx.test.annotation.UiThreadTest;
+import androidx.test.core.app.ActivityScenario;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+
+
+@RunWith(AndroidJUnit4.class)
+public class StateTrackerTest {
+
+    private static final String WIDGET_CATEGORY_NONE = "None";
+    private static final String WIDGET_CATEGORY_SCROLL = "Scroll";
+    private static final String WIDGET_STATE_IDLE = "Idle";
+    private static final String WIDGET_STATE_SCROLLING = "Scrolling";
+    private StateTracker mStateTracker;
+    private Choreographer mChoreographer;
+
+    @Rule
+    public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
+    /**
+     * Start an empty activity so choreographer won't return -1 for vsyncid.
+     */
+    private static ActivityScenario<EmptyActivity> sEmptyActivityRule;
+
+    @BeforeClass
+    public static void classSetup() {
+        sEmptyActivityRule = ActivityScenario.launch(EmptyActivity.class);
+    }
+
+    @AfterClass
+    public static void classTearDown() {
+        sEmptyActivityRule.close();
+    }
+
+    @Before
+    @UiThreadTest
+    public void setup() {
+        mChoreographer = Choreographer.getInstance();
+        mStateTracker = new StateTracker(mChoreographer);
+    }
+
+    /**
+     * Check that the start vsyncid is added when the state is first added and end vsyncid is
+     * set to the default value, indicating it has not been updated.
+     */
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_DETAILED_APP_JANK_METRICS_API)
+    public void addWidgetState_VerifyStateHasStartVsyncId() {
+        mStateTracker.putState(WIDGET_CATEGORY_SCROLL, WIDGET_STATE_SCROLLING,
+                "addWidgetState_VerifyStateHasStartVsyncId");
+
+        ArrayList<StateData> stateList = new ArrayList<StateData>();
+        mStateTracker.retrieveAllStates(stateList);
+        StateData stateData = stateList.get(0);
+
+        assertTrue(stateData.mVsyncIdStart > 0);
+        assertTrue(stateData.mVsyncIdEnd == Long.MAX_VALUE);
+    }
+
+    /**
+     * Check that the end vsyncid is added when the state is removed.
+     */
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_DETAILED_APP_JANK_METRICS_API)
+    public void removeWidgetState_VerifyStateHasEndVsyncId() {
+
+        mStateTracker.putState(WIDGET_CATEGORY_SCROLL, WIDGET_STATE_SCROLLING,
+                "removeWidgetState_VerifyStateHasEndVsyncId");
+        mStateTracker.removeState(WIDGET_CATEGORY_SCROLL, WIDGET_STATE_SCROLLING,
+                "removeWidgetState_VerifyStateHasEndVsyncId");
+
+        ArrayList<StateData> stateList = new ArrayList<StateData>();
+        mStateTracker.retrieveAllStates(stateList);
+        StateData stateData = stateList.get(0);
+
+        assertTrue(stateData.mVsyncIdStart > 0);
+        assertTrue(stateData.mVsyncIdEnd != Long.MAX_VALUE);
+    }
+
+    /**
+     * Check that duplicate states are aggregated into only one active instance.
+     */
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_DETAILED_APP_JANK_METRICS_API)
+    public void addDuplicateStates_ConfirmStateCountOnlyOne() {
+        mStateTracker.putState(WIDGET_CATEGORY_SCROLL, WIDGET_STATE_SCROLLING,
+                "addDuplicateStates_ConfirmStateCountOnlyOne");
+
+        ArrayList<StateData> stateList = new ArrayList<>();
+        mStateTracker.retrieveAllStates(stateList);
+
+        assertEquals(stateList.size(), 1);
+
+        mStateTracker.putState(WIDGET_CATEGORY_SCROLL, WIDGET_STATE_SCROLLING,
+                "addDuplicateStates_ConfirmStateCountOnlyOne");
+
+        stateList.clear();
+
+        mStateTracker.retrieveAllStates(stateList);
+
+        assertEquals(stateList.size(), 1);
+    }
+
+    /**
+     * Check that correct distinct states are returned when all states are retrieved.
+     */
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_DETAILED_APP_JANK_METRICS_API)
+    public void addThreeStateChanges_ConfirmThreeStatesReturned() {
+        mStateTracker.putState(WIDGET_CATEGORY_SCROLL, WIDGET_STATE_SCROLLING,
+                "addThreeStateChanges_ConfirmThreeStatesReturned");
+        mStateTracker.putState(WIDGET_CATEGORY_SCROLL, WIDGET_STATE_SCROLLING,
+                "addThreeStateChanges_ConfirmThreeStatesReturned_01");
+        mStateTracker.putState(WIDGET_CATEGORY_SCROLL, WIDGET_STATE_SCROLLING,
+                "addThreeStateChanges_ConfirmThreeStatesReturned_02");
+
+        ArrayList<StateData> stateList = new ArrayList<>();
+        mStateTracker.retrieveAllStates(stateList);
+
+        assertEquals(stateList.size(), 3);
+    }
+
+    /**
+     * Confirm when states are added and removed the removed states are moved to the previousStates
+     * list and returned when retrieveAllStates is called.
+     */
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_DETAILED_APP_JANK_METRICS_API)
+    public void simulateAddingSeveralStates() {
+        for (int i = 0; i < 20; i++) {
+            mStateTracker.removeState(WIDGET_CATEGORY_SCROLL, WIDGET_STATE_SCROLLING,
+                    String.format("simulateAddingSeveralStates_%s", i - 1));
+            mStateTracker.putState(WIDGET_CATEGORY_SCROLL, WIDGET_STATE_SCROLLING,
+                    String.format("simulateAddingSeveralStates_%s", i));
+        }
+
+        ArrayList<StateData> stateList = new ArrayList<>();
+        mStateTracker.retrieveAllStates(stateList);
+
+        int countStatesWithEndVsync = 0;
+        for (int i = 0; i < stateList.size(); i++) {
+            if (stateList.get(i).mVsyncIdEnd != Long.MAX_VALUE) {
+                countStatesWithEndVsync++;
+            }
+        }
+
+        // The last state that was added would be an active state and should not have an associated
+        // end vsyncid.
+        assertEquals(19, countStatesWithEndVsync);
+    }
+
+    /**
+     * Confirm once a state has been attributed to a frame it has been removed from the previous
+     * state list.
+     */
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_DETAILED_APP_JANK_METRICS_API)
+    public void confirmProcessedStates_RemovedFromPreviousStateList() {
+        for (int i = 0; i < 20; i++) {
+            mStateTracker.removeState(WIDGET_CATEGORY_SCROLL, WIDGET_STATE_SCROLLING,
+                    String.format("simulateAddingSeveralStates_%s", i - 1));
+            mStateTracker.putState(WIDGET_CATEGORY_SCROLL, WIDGET_STATE_SCROLLING,
+                    String.format("simulateAddingSeveralStates_%s", i));
+
+            if (i == 19) {
+                mStateTracker.removeState(WIDGET_CATEGORY_SCROLL, WIDGET_STATE_SCROLLING,
+                        String.format("simulateAddingSeveralStates_%s", i));
+            }
+        }
+
+        ArrayList<StateData> stateList = new ArrayList<>();
+        mStateTracker.retrieveAllStates(stateList);
+
+        assertEquals(20, stateList.size());
+
+        // Simulate processing all the states.
+        for (int i = 0; i < stateList.size(); i++) {
+            stateList.get(i).mProcessed = true;
+        }
+        // Clear out all processed states.
+        mStateTracker.stateProcessingComplete();
+
+        stateList.clear();
+
+        mStateTracker.retrieveAllStates(stateList);
+
+        assertEquals(0, stateList.size());
+    }
+}
diff --git a/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/DesktopModeAppHelper.kt b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/DesktopModeAppHelper.kt
index 4ac567c..1c6bd11 100644
--- a/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/DesktopModeAppHelper.kt
+++ b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/DesktopModeAppHelper.kt
@@ -109,11 +109,7 @@
         if (motionEventHelper.inputMethod == TOUCH
             && Flags.enableHoldToDragAppHandle()) {
             // Touch requires hold-to-drag.
-            val downTime = SystemClock.uptimeMillis()
-            motionEventHelper.actionDown(startX, startY, time = downTime)
-            SystemClock.sleep(100L) // hold for 100ns before starting the move.
-            motionEventHelper.actionMove(startX, startY, startX, endY, 100, downTime = downTime)
-            motionEventHelper.actionUp(startX, endY, downTime = downTime)
+            motionEventHelper.holdToDrag(startX, startY, startX, endY, steps = 100)
         } else {
             device.drag(startX, startY, startX, endY, 100)
         }
diff --git a/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/MotionEventHelper.kt b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/MotionEventHelper.kt
index 86a0b0f..1fe6088 100644
--- a/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/MotionEventHelper.kt
+++ b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/MotionEventHelper.kt
@@ -54,7 +54,15 @@
         injectMotionEvent(ACTION_UP, x, y, downTime = downTime)
     }
 
-    fun actionMove(startX: Int, startY: Int, endX: Int, endY: Int, steps: Int, downTime: Long) {
+    fun actionMove(
+        startX: Int,
+        startY: Int,
+        endX: Int,
+        endY: Int,
+        steps: Int,
+        downTime: Long,
+        withMotionEventInjectDelay: Boolean = false
+    ) {
         val incrementX = (endX - startX).toFloat() / (steps - 1)
         val incrementY = (endY - startY).toFloat() / (steps - 1)
 
@@ -65,9 +73,33 @@
 
             val moveEvent = getMotionEvent(downTime, time, ACTION_MOVE, x, y)
             injectMotionEvent(moveEvent)
+            if (withMotionEventInjectDelay) {
+                SystemClock.sleep(MOTION_EVENT_INJECTION_DELAY_MILLIS)
+            }
         }
     }
 
+    /**
+     * Drag from [startX], [startY] to [endX], [endY] with a "hold" period after touching down
+     * and before moving.
+     */
+    fun holdToDrag(startX: Int, startY: Int, endX: Int, endY: Int, steps: Int) {
+        val downTime = SystemClock.uptimeMillis()
+        actionDown(startX, startY, time = downTime)
+        SystemClock.sleep(100L) // Hold before dragging.
+        actionMove(
+            startX,
+            startY,
+            endX,
+            endY,
+            steps,
+            downTime,
+            withMotionEventInjectDelay = true
+        )
+        SystemClock.sleep(REGULAR_CLICK_LENGTH)
+        actionUp(startX, endX, downTime)
+    }
+
     private fun injectMotionEvent(
         action: Int,
         x: Int,
@@ -120,4 +152,9 @@
         event.displayId = 0
         return event
     }
+
+    companion object {
+        private const val MOTION_EVENT_INJECTION_DELAY_MILLIS = 5L
+        private const val REGULAR_CLICK_LENGTH = 100L
+    }
 }
\ No newline at end of file
diff --git a/tests/Input/res/xml/keyboard_glyph_maps.xml b/tests/Input/res/xml/keyboard_glyph_maps.xml
index d0616ff..42561c1 100644
--- a/tests/Input/res/xml/keyboard_glyph_maps.xml
+++ b/tests/Input/res/xml/keyboard_glyph_maps.xml
@@ -19,4 +19,8 @@
         androidprv:glyphMap="@xml/test_glyph_map"
         androidprv:vendorId="0x1234"
         androidprv:productId="0x3456" />
+    <keyboard-glyph-map
+        androidprv:glyphMap="@xml/test_glyph_map2"
+        androidprv:vendorId="0x1235"
+        androidprv:productId="0x3457" />
 </keyboard-glyph-maps>
\ No newline at end of file
diff --git a/tests/Input/res/xml/test_glyph_map2.xml b/tests/Input/res/xml/test_glyph_map2.xml
new file mode 100644
index 0000000..7a7c1ac
--- /dev/null
+++ b/tests/Input/res/xml/test_glyph_map2.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright 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.
+  -->
+<keyboard-glyph-map xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
+    <key-glyph
+        androidprv:keycode="KEYCODE_BACK"
+        androidprv:glyphDrawable="@drawable/test_key_drawable" />
+    <modifier-glyph
+        androidprv:modifier="META"
+        androidprv:glyphDrawable="@drawable/test_modifier_drawable" />
+    <function-row-key androidprv:keycode="KEYCODE_EMOJI_PICKER" />
+    <hardware-defined-shortcut
+        androidprv:keycode="KEYCODE_1"
+        androidprv:modifierState="FUNCTION"
+        androidprv:outKeycode="KEYCODE_BACK" />
+    <hardware-defined-shortcut
+        androidprv:keycode="KEYCODE_2"
+        androidprv:modifierState="FUNCTION|META"
+        androidprv:outKeycode="KEYCODE_HOME" />
+</keyboard-glyph-map>
\ No newline at end of file
diff --git a/tests/Input/src/com/android/server/input/InputGestureManagerTests.kt b/tests/Input/src/com/android/server/input/InputGestureManagerTests.kt
new file mode 100644
index 0000000..01c56b7
--- /dev/null
+++ b/tests/Input/src/com/android/server/input/InputGestureManagerTests.kt
@@ -0,0 +1,155 @@
+/*
+ * Copyright 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.input
+
+import android.hardware.input.InputGestureData
+import android.hardware.input.InputManager
+import android.hardware.input.KeyGestureEvent
+import android.platform.test.annotations.Presubmit
+import android.view.KeyEvent
+import org.junit.Assert.assertEquals
+import org.junit.Before
+import org.junit.Test
+
+/**
+ * Tests for custom keyboard glyph map configuration.
+ *
+ * Build/Install/Run:
+ * atest InputTests:CustomInputGestureManagerTests
+ */
+@Presubmit
+class InputGestureManagerTests {
+
+    companion object {
+        const val USER_ID = 1
+    }
+
+    private lateinit var inputGestureManager: InputGestureManager
+
+    @Before
+    fun setup() {
+        inputGestureManager = InputGestureManager()
+    }
+
+    @Test
+    fun addRemoveCustomGesture() {
+        val customGesture = InputGestureData.Builder()
+            .setTrigger(
+                InputGestureData.createKeyTrigger(
+                    KeyEvent.KEYCODE_H,
+                    KeyEvent.META_META_ON
+                )
+            )
+            .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_HOME)
+            .build()
+        val result = inputGestureManager.addCustomInputGesture(USER_ID, customGesture)
+        assertEquals(InputManager.CUSTOM_INPUT_GESTURE_RESULT_SUCCESS, result)
+        assertEquals(
+            listOf(customGesture),
+            inputGestureManager.getCustomInputGestures(USER_ID)
+        )
+
+        inputGestureManager.removeCustomInputGesture(USER_ID, customGesture)
+        assertEquals(
+            listOf<InputGestureData>(),
+            inputGestureManager.getCustomInputGestures(USER_ID)
+        )
+    }
+
+    @Test
+    fun removeNonExistentGesture() {
+        val customGesture = InputGestureData.Builder()
+            .setTrigger(
+                InputGestureData.createKeyTrigger(
+                    KeyEvent.KEYCODE_H,
+                    KeyEvent.META_META_ON
+                )
+            )
+            .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_HOME)
+            .build()
+        val result = inputGestureManager.removeCustomInputGesture(USER_ID, customGesture)
+        assertEquals(InputManager.CUSTOM_INPUT_GESTURE_RESULT_ERROR_DOES_NOT_EXIST, result)
+        assertEquals(
+            listOf<InputGestureData>(),
+            inputGestureManager.getCustomInputGestures(USER_ID)
+        )
+    }
+
+    @Test
+    fun addAlreadyExistentGesture() {
+        val customGesture = InputGestureData.Builder()
+            .setTrigger(
+                InputGestureData.createKeyTrigger(
+                    KeyEvent.KEYCODE_H,
+                    KeyEvent.META_META_ON
+                )
+            )
+            .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_HOME)
+            .build()
+        inputGestureManager.addCustomInputGesture(USER_ID, customGesture)
+        val customGesture2 = InputGestureData.Builder()
+            .setTrigger(
+                InputGestureData.createKeyTrigger(
+                    KeyEvent.KEYCODE_H,
+                    KeyEvent.META_META_ON
+                )
+            )
+            .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_BACK)
+            .build()
+        val result = inputGestureManager.addCustomInputGesture(USER_ID, customGesture2)
+        assertEquals(InputManager.CUSTOM_INPUT_GESTURE_RESULT_ERROR_ALREADY_EXISTS, result)
+        assertEquals(
+            listOf(customGesture),
+            inputGestureManager.getCustomInputGestures(USER_ID)
+        )
+    }
+
+    @Test
+    fun addRemoveAllExistentGestures() {
+        val customGesture = InputGestureData.Builder()
+            .setTrigger(
+                InputGestureData.createKeyTrigger(
+                    KeyEvent.KEYCODE_H,
+                    KeyEvent.META_META_ON
+                )
+            )
+            .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_HOME)
+            .build()
+        inputGestureManager.addCustomInputGesture(USER_ID, customGesture)
+        val customGesture2 = InputGestureData.Builder()
+            .setTrigger(
+                InputGestureData.createKeyTrigger(
+                    KeyEvent.KEYCODE_DEL,
+                    KeyEvent.META_META_ON
+                )
+            )
+            .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_BACK)
+            .build()
+        inputGestureManager.addCustomInputGesture(USER_ID, customGesture2)
+
+        assertEquals(
+            listOf(customGesture, customGesture2),
+            inputGestureManager.getCustomInputGestures(USER_ID)
+        )
+
+        inputGestureManager.removeAllCustomInputGestures(USER_ID)
+        assertEquals(
+            listOf<InputGestureData>(),
+            inputGestureManager.getCustomInputGestures(USER_ID)
+        )
+    }
+}
\ No newline at end of file
diff --git a/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt b/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt
index 787ae06..19dea0c 100644
--- a/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt
+++ b/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt
@@ -799,6 +799,70 @@
     }
 
     @Test
+    @EnableFlags(
+        com.android.hardware.input.Flags.FLAG_KEYBOARD_A11Y_SHORTCUT_CONTROL,
+        com.android.hardware.input.Flags.FLAG_KEYBOARD_A11Y_BOUNCE_KEYS_FLAG,
+        com.android.hardware.input.Flags.FLAG_KEYBOARD_A11Y_SLOW_KEYS_FLAG,
+        com.android.hardware.input.Flags.FLAG_KEYBOARD_A11Y_STICKY_KEYS_FLAG,
+        com.android.hardware.input.Flags.FLAG_KEYBOARD_A11Y_MOUSE_KEYS
+    )
+    fun testKeyboardAccessibilityToggleShortcutPress() {
+        val keyGestureController = KeyGestureController(context, testLooper.looper)
+        testKeyGestureInternal(
+            keyGestureController,
+            TestData(
+                "Meta + Alt + 3 -> Toggle Bounce Keys",
+                intArrayOf(
+                    KeyEvent.KEYCODE_META_LEFT,
+                    KeyEvent.KEYCODE_ALT_LEFT,
+                    KeyEvent.KEYCODE_3
+                ),
+                KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_BOUNCE_KEYS,
+                intArrayOf(KeyEvent.KEYCODE_3),
+                KeyEvent.META_META_ON or KeyEvent.META_ALT_ON,
+                intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)))
+        testKeyGestureInternal(
+            keyGestureController,
+            TestData(
+                "Meta + Alt + 4 -> Toggle Mouse Keys",
+                intArrayOf(
+                    KeyEvent.KEYCODE_META_LEFT,
+                    KeyEvent.KEYCODE_ALT_LEFT,
+                    KeyEvent.KEYCODE_4
+                ),
+                KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_MOUSE_KEYS,
+                intArrayOf(KeyEvent.KEYCODE_4),
+                KeyEvent.META_META_ON or KeyEvent.META_ALT_ON,
+                intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)))
+        testKeyGestureInternal(
+            keyGestureController,
+            TestData(
+                "Meta + Alt + 5 -> Toggle Sticky Keys",
+                intArrayOf(
+                    KeyEvent.KEYCODE_META_LEFT,
+                    KeyEvent.KEYCODE_ALT_LEFT,
+                    KeyEvent.KEYCODE_5
+                ),
+                KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_STICKY_KEYS,
+                intArrayOf(KeyEvent.KEYCODE_5),
+                KeyEvent.META_META_ON or KeyEvent.META_ALT_ON,
+                intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)))
+        testKeyGestureInternal(
+            keyGestureController,
+            TestData(
+                "Meta + Alt + 6 -> Toggle Slow Keys",
+                intArrayOf(
+                    KeyEvent.KEYCODE_META_LEFT,
+                    KeyEvent.KEYCODE_ALT_LEFT,
+                    KeyEvent.KEYCODE_6
+                ),
+                KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_SLOW_KEYS,
+                intArrayOf(KeyEvent.KEYCODE_6),
+                KeyEvent.META_META_ON or KeyEvent.META_ALT_ON,
+                intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)))
+    }
+
+    @Test
     @EnableFlags(com.android.window.flags.Flags.FLAG_ENABLE_MOVE_TO_NEXT_DISPLAY_SHORTCUT)
     fun testMoveToNextDisplay() {
         val keyGestureController = KeyGestureController(context, testLooper.looper)
diff --git a/tests/Input/src/com/android/server/input/KeyboardGlyphManagerTests.kt b/tests/Input/src/com/android/server/input/KeyboardGlyphManagerTests.kt
index ff8a9ba..5da0beb 100644
--- a/tests/Input/src/com/android/server/input/KeyboardGlyphManagerTests.kt
+++ b/tests/Input/src/com/android/server/input/KeyboardGlyphManagerTests.kt
@@ -59,6 +59,9 @@
         const val DEVICE_ID = 1
         const val VENDOR_ID = 0x1234
         const val PRODUCT_ID = 0x3456
+        const val DEVICE_ID2 = 2
+        const val VENDOR_ID2 = 0x1235
+        const val PRODUCT_ID2 = 0x3457
         const val PACKAGE_NAME = "KeyboardLayoutManagerTests"
         const val RECEIVER_NAME = "DummyReceiver"
     }
@@ -96,8 +99,11 @@
             .thenReturn(inputManager)
 
         keyboardDevice = createKeyboard(DEVICE_ID, VENDOR_ID, PRODUCT_ID, 0, "", "")
-        Mockito.`when`(inputManagerRule.mock.inputDeviceIds).thenReturn(intArrayOf(DEVICE_ID))
+        Mockito.`when`(inputManagerRule.mock.inputDeviceIds).thenReturn(intArrayOf(DEVICE_ID, DEVICE_ID2))
         Mockito.`when`(inputManagerRule.mock.getInputDevice(DEVICE_ID)).thenReturn(keyboardDevice)
+
+        val keyboardDevice2 = createKeyboard(DEVICE_ID2, VENDOR_ID2, PRODUCT_ID2, 0, "", "")
+        Mockito.`when`(inputManagerRule.mock.getInputDevice(DEVICE_ID2)).thenReturn(keyboardDevice2)
     }
 
     private fun setupBroadcastReceiver() {
@@ -143,6 +149,10 @@
             "Glyph map for test keyboard(deviceId=$DEVICE_ID) must exist",
             keyboardGlyphManager.getKeyGlyphMap(DEVICE_ID)
         )
+        assertNotNull(
+            "Glyph map for test keyboard(deviceId=$DEVICE_ID2) must exist",
+            keyboardGlyphManager.getKeyGlyphMap(DEVICE_ID2)
+        )
         assertNull(
             "Glyph map for non-existing keyboard must be null",
             keyboardGlyphManager.getKeyGlyphMap(-2)
@@ -158,6 +168,7 @@
 
         assertNotNull(glyphMap.getDrawableForModifier(context, KeyEvent.KEYCODE_META_LEFT))
         assertNotNull(glyphMap.getDrawableForModifier(context, KeyEvent.KEYCODE_META_RIGHT))
+        assertNotNull(glyphMap.getDrawableForModifierState(context, KeyEvent.META_META_ON))
 
         val functionRowKeys = glyphMap.functionRowKeys
         assertEquals(1, functionRowKeys.size)
diff --git a/tests/TouchLatency/app/src/main/res/values/styles.xml b/tests/TouchLatency/app/src/main/res/values/styles.xml
index 5058331..fa352cf 100644
--- a/tests/TouchLatency/app/src/main/res/values/styles.xml
+++ b/tests/TouchLatency/app/src/main/res/values/styles.xml
@@ -18,7 +18,7 @@
     <!-- Base application theme. -->
     <style name="AppTheme" parent="Theme.MaterialComponents.Light.DarkActionBar">
         <!-- Customize your theme here. -->
-        <item name="android:windowOptOutEdgeToEdgeEnforcement">true</item>
+        <item name="android:windowLayoutInDisplayCutoutMode">default</item>
     </style>
 
 </resources>
diff --git a/tests/vcn/OWNERS b/tests/vcn/OWNERS
index 2441e77..937699a 100644
--- a/tests/vcn/OWNERS
+++ b/tests/vcn/OWNERS
@@ -1,7 +1,6 @@
 set noparent
 
-benedictwong@google.com
-ckesting@google.com
 evitayan@google.com
-junyin@google.com
 nharold@google.com
+benedictwong@google.com #{LAST_RESORT_SUGGESTION}
+yangji@google.com #{LAST_RESORT_SUGGESTION}
diff --git a/tools/aapt2/cmd/Link.cpp b/tools/aapt2/cmd/Link.cpp
index 498e431..232b402 100644
--- a/tools/aapt2/cmd/Link.cpp
+++ b/tools/aapt2/cmd/Link.cpp
@@ -1574,7 +1574,10 @@
   // If the file path ends with .flata, .jar, .jack, or .zip the file is treated
   // as ZIP archive and the files within are merged individually.
   // Otherwise the file is processed on its own.
-  bool MergePath(const std::string& path, bool override) {
+  bool MergePath(std::string path, bool override) {
+    if (path.size() > 2 && util::StartsWith(path, "'") && util::EndsWith(path, "'")) {
+      path = path.substr(1, path.size() - 2);
+    }
     if (util::EndsWith(path, ".flata") || util::EndsWith(path, ".jar") ||
         util::EndsWith(path, ".jack") || util::EndsWith(path, ".zip")) {
       return MergeArchive(path, override);